Guides & Tutorials

Migrating Breaking Changes in SvelteKit

SvelteKit has gone through a few breaking changes recently including an entire routing overhaul, changes to the load API, and a switch to the Vite CLI. If you’ve used it in the past, it may look quite a bit different than it used to. Just looking at the August updates, there were ten breaking changes. It’s a lot to keep up with, so this post will review the biggest breaking changes and navigate you through migrating an app to get it up to date.

So Long File Based Routing

By far the biggest change in SvelteKit is doing away with file-based routing. There is still a routes directory where you will create your routes, but they are now directory based and require a + prefix to generate a route.

Routing The Old Way

Before, any file added to the routes directory would create a route at that name. This made it difficult to colocate files without more confusing syntax. Double underscores, __ indicated a layout or error route, and a single underscore, _ would skip creating it as a route. Things were messy and nothing was grouped together.

πŸ“‚ src
β”” πŸ“‚ routes
  β”œ πŸ“„ index.svelte
  β”œ πŸ“„ about.svelte
  β”œ πŸ“„ __layout.svelte
  β”œ πŸ“„ __layout-nested.svelte
  β”œ πŸ“„ __error.svelte
  β”” πŸ“‚ nested-route
    β”” πŸ“„ _component-for-nested.svelte  
    β”” πŸ“„ index@nested.svelte

Routing The New Way

Now all routes are directory based, other than the home or root route, all new routes need to be in a folder. The old index.svelte has gone away in favor of +page.svelte to indicate you are deliberately creating a route. You also no longer need the underscore, _, to colocate a component to a route. It’s much clearer what route each file is attached to in the new routing structure.

πŸ“‚ src
β”” πŸ“‚ routes
  β”œ πŸ“„ +page.svelte
  β”œ πŸ“„ +layout.svelte
  β”œ πŸ“„ +error.svelte
  β””  πŸ“‚ about
     β”œ πŸ“„ coc.svelte
     β”œ πŸ“„ +page.svelte
     β”” πŸ“„ +layout.svelte

Resources

To find out more about why these changes happened, there are some really good conversations that happened on GitHub and the documentation walks through the new setup.

Layout Overhaul

Layouts in Svelte have gone through a major overhaul. No longer do multiple layouts live in the root of the routes directory, then naming them on a file such as index@nested.svelte. Basic layouts are now +layout.svelte files and will inherit from the root layout no matter how nested they are.

Layouts The Old Way

πŸ“‚ src
β”” πŸ“‚ routes
  β”œ πŸ“„ index.svelte
  β”œ πŸ“„ __layout.svelte
  β”œ πŸ“„ __layout-nested.svelte
  β”” πŸ“‚ nested-route
    β”” πŸ“„ index@nested.svelte

Layouts The New Way

πŸ“‚ src
β”” πŸ“‚ routes
  β”œ πŸ“„ +page.svelte
  β”œ πŸ“„ +layout.svelte
  β””  πŸ“‚ about
     β”œ πŸ“„ +page.svelte
     β”” πŸ“„ +layout.svelte // will inherit the src/routes/+layout.svelte layout

Group Layouts

There is also a new convention to share layouts with a folder wrapped in parenthesis()called a group directory. Group directories do not affect the URL pathname of the nested routes, it essentially acts as a root route. A +page.svelte file can be added to a (group) directory root if the / or home route should be the group. Since layouts inherit all the way down, you may want to use a different layout in a nested route. To do that we can still use the named layout syntax of +page@nested.svelte where nested is the name of the layout you wish to use. To opt out of a layout entirely and reset it, create a +layout@.svelte file in the directory by the +page.svelte file that needs reset.

πŸ“‚ src
β”” πŸ“‚ routes
  β”œ πŸ“„ +layout.svelte
  β””  πŸ“‚ (marketing) // group routes
     β”œ πŸ“‚ about
       β”œ πŸ“‚ embed
         β”œ πŸ“„ +page.svelte
         β”” πŸ“„ +layout@(marketing).svelte // this will skip the about layout
       β”” πŸ“„ +layout.svelte
     β”œ πŸ“‚ coc
       β”œ πŸ“„ +page.svelte
       β”” πŸ“„ +layout@.svelte //this will opt out of all layouts
     β”œ πŸ“„ +page.svelte // lives at / not /marketing
     β”” πŸ“„ +layout.svelte

Resources

These changes were not the most straightforward and even the documentation is a little difficult to parse right now, but it is there. I found the discussion of the changes on GitHub to be more helpful and easier to understand.

Removing Confusion on Load

The load function is how a SvelteKit route receives outside data, such as from a database or 3rd party API. Before there was always confusion around whether the load function ran on the server or the client and was never clear if secrets were leaking through to the client. The load function has now moved out of the .svelte file and into an endpoint file.

Load The Old Way

Before, the load function lived in the context="module" script tag inside of the .svelte file or the data for the route was magically returned from an endpoint. Not all that long ago, another change occurred to the way data was loaded into a component. While the official name was page endpoints, us diehards kept fighting for the obviously cooler sounding shadow endpoints. These automagically loaded the data from the endpoint directly into the .svelte route by returning the data and then exporting a prop of the same name in the route.

πŸ“‚ src
β”” πŸ“‚ routes
  β”œ πŸ“„ index.svelte
  β”œ πŸ“„ index.js
  β”œ πŸ“„ about.svelte
  β”œ πŸ“„ about.js
  β”œ πŸ“„ __layout.svelte
  β”œ πŸ“„ __layout-nested.svelte
  β”œ πŸ“„ __error.svelte
  β”” πŸ“‚ nested-route
    β”œ πŸ“„ index.js
    β”” πŸ“„ index@nested.svelte
// in a .svelte route
<script context="module">
  export const load = () => {
    let propName = "arbitrary data"
    return {
      props: {
	propName
      }
    }
  }
</script>

<script>
  export let propName
</script>

<h1>Use the {propName}</h1>

Load The New Way

Now the load function has moved into the endpoint file and the magical loading of data into the .svelte file still exists and passes to the route with the data prop. There are two new files you can use to load data into a route. The +page.js (orΒ +page.ts for TypeScript) runs on the server during server-side rendering and again in the client when client-side navigating. There is also a +page.server.js that will only run on the server making it safe for API keys and secure SDKs you don’t want leaked to the client. You also no longer need to return the data inside of a props object. You determine where that data loads, either only on the server or both the server and client. In the +page.svelte route file, the prop is simply named data now.

πŸ“‚ src
β”” πŸ“‚ routes
  β”œ πŸ“„ +page.svelte
  β”œ πŸ“„ +page.js
  β”œ πŸ“„ +layout.svelte
  β”œ πŸ“„ +error.svelte
  β””  πŸ“‚ about
     β”œ πŸ“„ +page.svelte
     β”œ πŸ“„ +page.server.js
     β”” πŸ“„ +layout.svelte
// in a +page.js or +page.server.js endpoint
// copy over your load function from before
export const load = () => {
  let propName = "arbitrary data"
  return {
    // remove the props object 
    propName
  }
}
// in a +page.svelte route
<script>
  export let data
</script>

<h1>Use the {data.propName}</h1>

Page Behavior

The exports that dictated a pages behavior have also moved from the <script context="module"> into the endpoint file +page.js.

Resources

Here are some links to the documentation for the new endpoint files and loading data.

All in on Vite

SvelteKit has gone all in on the Vite ecosystem by pulling out the Vite config and becoming a Vite plugin. Way back in the day SvelteKit ran on Snowpack. They made the decision to switch after Vite version 2 came out in 2021 and added support for other frameworks besides Vue. That was just the underlying build tool and SvelteKit was still passing along the Vite configuration through its own svelte.config.js file and using its own sveltekit CLI command for running the dev and build scripts. This was using an experimental method and the goal was to move to a more standard way that frameworks typically interact with Vite, through a plugin. This allows for first-party support for tools like Vitest and Storybook that look for a Vite config file. With the new changes, SvelteKit is just a Vite project that uses the @sveltejs/kit/vite plugin and switched to the Vite CLI for the dev and build scripts. There is now a vite.config.js file at the root of a SvelteKit project, that includes the plugin along with any other Vite configuration.

Vite The Old Way

// svelte.config.js
import adapter from '@sveltejs/adapter-auto';
import preprocess from 'svelte-preprocess';

/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://github.com/sveltejs/svelte-preprocess
// for more information about preprocessors
preprocess: preprocess(),

  kit: {
    adapter: adapter(),
    vite: {
      // vite config stuff
    }
  };

export default config;
// package.json
{
  "scripts": {
    "dev": "svelte-kit dev",
    "build": "svelte-kit build",
    "preview": "svelte-kit preview",
  }
}

Vite The New Way

// vite.config.js
import { sveltekit } from '@sveltejs/kit/vite';

export default {
  plugins: [ sveltekit() ]
};
// package.json
{
  "scripts": {
	  "dev": "vite dev",
	  "build": "vite build",
	  "preview": "vite preview",
  }
}

Migrating Changes

With big change always comes a lot of frustration for users. The maintainers of SvelteKit took this in mind and created an entire migration guide and a command, npx svelte-migrate routes, that attempts to make a lot of the changes for you. The script will rename the files inside the routes directory, comments out anything in a context module script tag, and throws errors to tell you to update to the data prop. However, no tool can take care of everything and there will still be manual changes that need to updated. Depending on how long it’s been since you’ve upgraded, you may have several things to change. Here are the overview steps to follow.

  1. Remove old lock files and node_modules.

    rm -rf node_modules package-lock.json # or yarn-lock or pnpm-lock
  2. Update dependencies through your package manager.

    npm i # or yarn or pnpm
  3. Run the migration command. The routes will be renamed and placed in the new directory structure and endpoint files will attempt to move to the appropriate type.

    npx svelte-migrate routes
  4. Update any props in +page.svelte files to data.

    - export let propName
    + export let data
  5. Remove any <script context="module"> stuff over to an endpoint file.

    // in a +page.js or +page.server.js endpoint
    // copy over your load function from the .svelte file
    export const load = () => {
      let propName = "arbitrary data"
      return {
        // remove the props object 
        propName
      }
    }
  6. Make sure your scripts have been updated to the Vite CLI in the package.json.

    // package.json
    {
      "scripts": {
        "dev": "vite dev",
        "build": "vite build",
        "preview": "vite preview",
      }
    }
  7. Try to run it!

    npm run dev # or yarn or pnpm

These steps are just a general overview of the biggest changes. Depending on when you last upgraded, there may be other steps to follow. SvelteKit errors are pretty good and may help walk you through any other changes you need to make. There may also be unique situations in your app that will need more work. With the addition of the +page.server.js file, ensure you are loading your data where you need it. This will hopefully get you most of the way there.

Resources

To find out more information about the migration, check out the migration guide and you can always view the most recent updates in the SvelteKit Docs.

Moving Forward

With so many changes lately in SvelteKit, a lot of us are hoping to see the light at the end of the tunnel soon. Let’s hope these rapid changes mean we are getting close to 1.0. Hopefully this post helped bridge the gap to the latest changes. Go forth and migrate!

Other Resources

Here are a few extra resources to continue getting up to date with everything in SvelteKit.

A few podcasts have been talking about the recent changes. If you prefer listening to reading check out these Svelte Radio and SyntaxFM episodes.

Keep reading

Recent posts

How do the best dev and marketing teams work together?