Svelte
03 / 07

SvelteKit & Routing

SvelteKit & Routing

SvelteKit is the official application framework for Svelte. It provides routing, server-side rendering, API endpoints, and a powerful filesystem-based routing system.

Filesystem-Based Routing

// Project structure defines routes
src/routes/
  +page.svelte              → /
  about/+page.svelte        → /about
  blog/+page.svelte         → /blog
  blog/[slug]/+page.svelte  → /blog/:slug
  users/[id]/+page.svelte   → /users/:id
  admin/
    +layout.svelte          → Layout for /admin/*
    +page.svelte            → /admin
    users/+page.svelte      → /admin/users

// src/routes/+page.svelte (home page)
<script>
  export let data;  // From load function
</script>

<h1>Welcome to SvelteKit</h1>
<p>{data.message}</p>

// src/routes/blog/[slug]/+page.svelte
<script>
  export let data;
</script>

<article>
  <h1>{data.post.title}</h1>
  <div>{@html data.post.content}</div>
</article>

Loading Data

// src/routes/+page.ts (or +page.server.ts)
import type { PageLoad } from './$types';

export const load: PageLoad = async ({ params, url, fetch }) => {
  return {
    message: 'Hello from load function'
  };
};

// src/routes/blog/[slug]/+page.ts
export const load: PageLoad = async ({ params, fetch }) => {
  const response = await fetch(`/api/posts/${params.slug}`);
  const post = await response.json();
  
  return {
    post
  };
};

// Server-only load function (+page.server.ts)
import type { PageServerLoad } from './$types';
import { db } from '$lib/server/database';

export const load: PageServerLoad = async ({ params }) => {
  // Can access database, environment variables, etc.
  const post = await db.posts.findOne({ slug: params.slug });
  
  return {
    post
  };
};

Layouts

// src/routes/+layout.svelte (root layout)
<script>
  export let data;
</script>

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

<main>
  <slot />  <!-- Child pages render here -->
</main>

<footer>
  <p>© 2024 My App</p>
</footer>

// src/routes/blog/+layout.svelte (nested layout)
<script>
  export let data;
</script>

<div class="blog-layout">
  <aside>
    <h3>Categories</h3>
    {#each data.categories as category}
      <a href="/blog/category/{category.slug}">{category.name}</a>
    {/each}
  </aside>
  
  <div class="content">
    <slot />  <!-- Blog pages render here -->
  </div>
</div>

Navigation

<script>
  import { goto, invalidate, invalidateAll } from '$app/navigation';
  import { page } from '$app/stores';
  
  // Current page data
  $: console.log('Current path:', $page.url.pathname);
  $: console.log('Params:', $page.params);
  $: console.log('Search params:', $page.url.searchParams);
  
  function navigateToPost(slug) {
    goto(`/blog/${slug}`);
  }
  
  function navigateWithOptions() {
    goto('/about', {
      replaceState: true,  // Replace history instead of push
      noScroll: true,      // Don't scroll to top
      keepFocus: true      // Keep focus on current element
    });
  }
  
  // Invalidate data
  function refresh() {
    invalidateAll();  // Rerun all load functions
  }
</script>

<!-- Declarative navigation -->
<a href="/about">About</a>
<a href="/blog/{post.slug}">Read More</a>

Form Actions

// src/routes/login/+page.server.ts
import type { Actions } from './$types';
import { fail, redirect } from '@sveltejs/kit';

export const actions: Actions = {
  login: async ({ request, cookies }) => {
    const data = await request.formData();
    const email = data.get('email');
    const password = data.get('password');
    
    // Validate
    if (!email || !password) {
      return fail(400, { email, missing: true });
    }
    
    // Authenticate
    const user = await authenticateUser(email, password);
    
    if (!user) {
      return fail(401, { email, incorrect: true });
    }
    
    // Set cookie
    cookies.set('session', user.sessionId, {
      path: '/',
      httpOnly: true,
      sameSite: 'strict',
      maxAge: 60 * 60 * 24 * 7  // 1 week
    });
    
    throw redirect(303, '/dashboard');
  }
};

// src/routes/login/+page.svelte
<script>
  import { enhance } from '$app/forms';
  export let form;  // Action response
</script>

<form method="POST" action="?/login" use:enhance>
  <input name="email" type="email" value={form?.email ?? ''} />
  {#if form?.missing}
    <p class="error">Email and password required</p>
  {/if}
  {#if form?.incorrect}
    <p class="error">Invalid credentials</p>
  {/if}
  
  <input name="password" type="password" />
  <button>Login</button>
</form>

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free