Understanding SvelteKit routes
+page, +page.server, +layout — that's it.
SvelteKit's filesystem routing, the three load patterns, and the form actions that make server mutations feel idiomatic.
The plus-prefix files.
SvelteKit uses +-prefixed filenames to mark special files inside route folders. +page.svelte: the route's UI. +page.ts: a load function that runs on both server and client (universal data loading).+page.server.ts: a load function that runs only on the server (database queries, secrets). +layout.svelte + companion ts files: parent layouts. +server.ts: an API route returning JSON.
The three load patterns.
Pattern 1: pure client data — fetch from the browser, no server involvement. Pattern 2: universal load — fetch from either side, useful for static content that should also hydrate on the client. Pattern 3: server-only load — runs on the server only, ships the result as JSON to the client. The choice is the same as Next.js's server/client component split, framed slightly differently. Use server-only for anything secret; universal for cacheable read data; client-only for genuinely client-state-dependent fetches.
A worked route.
A blog post route at routes/blog/[slug]. +page.server.ts exports a load function: export async function load({ params }) {
const post = await db.posts.findUnique({ where: { slug: params.slug } });
if (!post) error(404);
return { post };
} +page.svelte consumes via {export let data} or (Svelte 5) let { data } = $props(). The post object is fetched on the server and serialised into the page; no client-side fetch needed for the initial render.
Server load → page render
+page.server.ts → +page.svelte
The server load returns data; the page receives it as a prop.
load returns { post } → page uses data.post
= SSR with hydration
Form actions.
SvelteKit's standout feature for mutations: +page.server.ts can export an actions object whose keys are form action names. <form method="POST"> in the page submits to the default action; <form action="?/login"> submits to the named one. Each handler runs on the server, has access to the form data, returns a response. Works without JavaScript (progressive enhancement is automatic); enhances with JS to avoid the full reload.
Layouts compose top-down.
A +layout.svelte at any folder wraps the route and every nested route below. Nested layouts compose: the root layout wraps everything, an (app) route group's layout wraps just the authenticated app pages. Same idea as Next.js's layout.tsx; same default (persistent across navigation, doesn't re-render unless its own dependencies change).
SvelteKit vs Next.js, the honest comparison.
Same shape (filesystem routing, server-rendered pages, hydrated islands, form actions). SvelteKit's runtime is smaller (Svelte compiles to vanilla JS; React is heavier). Next.js has more deployment lock-in to Vercel (though self-hosting is possible) and more breaking changes per major. SvelteKit's ecosystem is smaller; React's wins for component-library availability. Pick based on what your team already knows; both are excellent frameworks.