SvelteKit essentials

Svelte · Advanced · 15 min

Real Svelte apps are usually SvelteKit apps; it adds routing, server rendering and data loading.

SvelteKit is the application framework for Svelte, built on Vite. Routes are folders in src/routes:

  • +page.svelte renders a page; src/routes/blog/[slug]/+page.svelte handles /blog/anything.
  • +layout.svelte wraps child routes; it renders them with {@render children()}.
  • +page.js or +page.server.js exports a `load` function that returns data for the page. The page receives it as let { data } = $props(). +page.server.js runs only on the server, so it can use secrets and databases; +page.js runs on the server for the first request and in the browser during client-side navigation.
  • +server.js exports GET, POST and other handlers for API endpoints.
  • +error.svelte renders errors thrown with error(404, 'Not found').

Form actions in +page.server.js handle <form method="POST"> submissions on the server; they work without JavaScript, and use:enhance from $app/forms upgrades them to fetch-based submissions.

Pages are server-rendered by default and then hydrated; after that, navigation happens in the browser, like an SPA. Per route you can set export const prerender = true for static pages or ssr = false for client-only pages.

Adapters produce output for a target: adapter-node, adapter-static, adapter-vercel, adapter-cloudflare and more. adapter-auto detects common platforms.

Example

// src/routes/todos/+page.server.js
import { fail } from '@sveltejs/kit';
import { db } from '$lib/server/db';         // server-only module

export async function load() {
  return { todos: await db.todos.all() };     // becomes `data` in the page
}

export const actions = {
  default: async ({ request }) => {
    const text = (await request.formData()).get('text');
    if (!text) return fail(400, { missing: true });
    await db.todos.add(String(text));
  },
};

// src/routes/todos/+page.svelte
// <script>
//   import { enhance } from '$app/forms';
//   let { data, form } = $props();
// </script>
// <form method="POST" use:enhance>
//   <input name="text" />{#if form?.missing}<p>Required</p>{/if}
// </form>
// {#each data.todos as t (t.id)}<p>{t.text}</p>{/each}

Remember

  • Routes are folders with +page.svelte, +layout.svelte, +server.js
  • load in +page(.server).js provides data; .server files never reach the browser
  • Form actions handle POSTs and work without JavaScript; use:enhance upgrades them
  • Adapters target Node, static hosting or serverless platforms

Watch out for

  • Importing database code into +page.js (universal) instead of +page.server.js, exposing it to the browser bundle.

Check yourself

  1. Where should a load function that queries the database with a secret key live?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: svelte.dev/docs/kit/load