Navigating Your App

Routing is one of the core components of SvelteKit.

Let’s create an about page at src/routes/about.svelte and link to the home page from it.

<h1>About</h1>
<p>SvelteKit is awesome!</p>
<a href="/">Go back home</a>

Unlike many other routing solutions in other app frameworks, SvelteKit doesn’t require a special component for your links. Plain old <a> tags will work fine!

Our About page is looking pretty bare though, especially compared to the index page. We want to make it fairly consistent across the app, but copying styles across multiple pages seems like a terrible idea.

Layouts

A routing solution wouldn’t be complete without layouts!

Let’s extract the common wrapping element out of src/routes/index.svelte

<script>
    import Counter from '$components/Counter.svelte';
</script>

<h1>Hello world!</h1>

<Counter/>
<p>Visit the <a href="https://svelte.dev">svelte.dev</a> to learn how to build Svelte apps.</p>

<style>
    /* Removed the :root and main styles */
    h1 {
        /* ... */
    }

    p {
        /* ... */
    }

    @media (min-width: 480px) {
        /* ... */
    }
</style>

…and into src/routes/$layout.svelte.

<main>
    <slot />
</main>

<style>
    :root {
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
    }

    main {
        text-align: center;
        padding: 1em;
        margin: 0 auto;
    }
</style>

This should be familiar to Sapper users: _layout.svelte was just renamed to $layout.svelte. This means that the underscore prefix is finally consistent across all your routes; they’ll be treated as private, ignored items.

This all works without needing to write any JavaScript!

Components

Our about page is looking spiffier now, but going back and forth between the two is a pain without links on the index page.

Instead of adding a link directly on the index page, let’s create our first component inside src/components/Nav.svelte.

<nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
</nav>

<style>
    nav {
        display: flex;
    }
    nav a {
        color: #ff3e00;
    }
    nav a + a {
        margin-left: 1rem;
    }
</style>

Let’s include it in our layout. Like Counter, we can import it using a special path provided by SvelteKit:

<script>
    import Nav from '$components/Nav.svelte';
</script>

<Nav />

<main>
    <slot />
</main>

<!-- styles -->

This $components path is an alias to your src/components folder, and remains consistent no matter how deep in your folders you go. Some of the magic is powered by Vite, which you can check out in your vite.config.cjs file.

Error Pages

Sometimes, things go wrong. We want to be able to handle it gracefully.

Let’s create our error handling page at src/routes/$error.svelte and test it out by going to a non-existent page.

<script>
    /** @type { number } */
    export let status;
    /** @type { Error } */
    export let error;
</script>

<main>
    <h1>{status}</h1>
    <p>Something went wrong!</p>
    <pre>{error.stack}</pre>
</main>

Dynamic Routes

If we could only serve pages at specific endpoints, our application would be pretty sad. We could use query parameters, but sometimes it’s nicer to have access to pretty routes.

To this end, you can create folders and files with [brackets].

  • src/routes/blog/[slug].svelte could serve subpages of blog, such as http://localhost:3000/blog/hello-world.
  • src/routes/[lang]/[slug].svelte could serve various language paths, such as http://localhost:3000/en-US/my-article

In the latter example, you would be able to read the dynamic parameters lang === 'en-US' and slug === 'my-article'. But where would you get that information from?