SvelteKit 5 Complete Guide: Runes, Routing & Deployment 2026
Master SvelteKit 5 with Svelte 5 runes, file-based routing, SSR, form actions, and deployment to Vercel and Cloudflare Pages.
SvelteKit 5 paired with Svelte 5βs rune system represents the most significant shift in the Svelte ecosystem since its inception. If you have been building with React/Next.js or Vue/Nuxt, SvelteKit offers a refreshing approach: less boilerplate, faster builds, and a compiler-first philosophy that ships minimal JavaScript to the browser. This guide walks you through everything from Svelte 5 reactivity primitives to production deployment.
What Changed in Svelte 5
Svelte 5 rewrote its reactivity model from the ground up. The old $: reactive statements and writable/readable stores still work (with a compatibility flag), but the new runes system is the recommended path forward. Runes are special compiler-recognized functions prefixed with $ that express reactive intent explicitly rather than implicitly.
$state β Reactive Variables
$state replaces let declarations that Svelte 4 made reactive via the compiler magic.
<script>
let count = $state(0);
let user = $state({ name: 'Alice', age: 30 });
</script>
<button onclick={() => count++}>
Clicked {count} times
</button>
<!-- Deep reactivity works out of the box -->
<button onclick={() => user.age++}>
Age: {user.age}
</button>
Unlike Svelte 4 where any top-level let was reactive, $state is explicit. This makes it easier to reason about what is reactive when reading code β and it works inside .svelte.js / .svelte.ts files too, not just .svelte components.
$derived β Computed Values
$derived replaces $: computedValue = expression reactive declarations.
<script>
let price = $state(100);
let quantity = $state(3);
// Recomputes whenever price or quantity changes
let total = $derived(price * quantity);
// Use $derived.by for multi-line logic
let discount = $derived.by(() => {
if (total > 250) return total * 0.1;
if (total > 150) return total * 0.05;
return 0;
});
let finalPrice = $derived(total - discount);
</script>
<p>Total: ${finalPrice.toFixed(2)}</p>
$effect β Side Effects
$effect replaces $: { sideEffect() } patterns and is closer to Reactβs useEffect, but without a dependency array β Svelte tracks dependencies automatically.
<script>
let query = $state('');
let results = $state([]);
$effect(() => {
// Runs whenever `query` changes
if (query.length < 2) {
results = [];
return;
}
const controller = new AbortController();
fetch(`/api/search?q=${query}`, { signal: controller.signal })
.then(r => r.json())
.then(data => { results = data; });
// Cleanup runs before the next execution
return () => controller.abort();
});
</script>
<input bind:value={query} placeholder="Search..." />
{#each results as result}
<div>{result.title}</div>
{/each}
$props β Component Props
$props replaces the export let pattern for declaring component props.
<script>
// Old Svelte 4 way:
// export let name;
// export let greeting = 'Hello';
// Svelte 5 runes way:
let { name, greeting = 'Hello', ...rest } = $props();
</script>
<p>{greeting}, {name}!</p>
SvelteKit File-Based Routing
SvelteKit uses a src/routes/ directory where every folder maps to a URL segment. The routing conventions are powerful once internalized.
src/routes/
βββ +page.svelte β /
βββ +layout.svelte β shared layout for all routes
βββ blog/
β βββ +page.svelte β /blog
β βββ +page.server.ts β load function (server-only)
β βββ [slug]/
β βββ +page.svelte β /blog/:slug
β βββ +page.server.ts
βββ api/
β βββ search/
β βββ +server.ts β /api/search (REST endpoint)
βββ (auth)/ β route group (no URL segment)
βββ login/
β βββ +page.svelte β /login
βββ register/
βββ +page.svelte β /register
Key file conventions:
+page.svelteβ the page component+layout.svelteβ wraps child routes with shared UI+page.server.tsβ server-only load function and form actions+page.tsβ universal load function (runs on server + client)+server.tsβ API route handler (GET, POST, etc.)+error.svelteβ error boundary for the route segment
Load Functions and Server-Side Rendering
SvelteKitβs SSR model revolves around load functions that run before the page renders.
Server Load Functions
// src/routes/blog/[slug]/+page.server.ts
import type { PageServerLoad } from './$types';
import { error } from '@sveltejs/kit';
export const load: PageServerLoad = async ({ params, fetch }) => {
const response = await fetch(`/api/posts/${params.slug}`);
if (!response.ok) {
error(404, 'Post not found');
}
const post = await response.json();
return { post };
};
<!-- src/routes/blog/[slug]/+page.svelte -->
<script>
let { data } = $props();
// data.post is typed and available immediately (SSR)
</script>
<article>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
</article>
Universal Load Functions
When you need the load to run on both server (for initial SSR) and client (for navigation), use +page.ts instead:
// src/routes/products/+page.ts
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ fetch, url }) => {
const category = url.searchParams.get('category') ?? 'all';
const products = await fetch(`/api/products?category=${category}`)
.then(r => r.json());
return { products, category };
};
Layout Load Functions
Layouts can also have load functions. Data returned flows down to child pages.
// src/routes/+layout.server.ts
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async ({ locals }) => {
return {
user: locals.user ?? null
};
};
Form Actions
Form actions are SvelteKitβs built-in solution for handling HTML form submissions with full progressive enhancement β they work without JavaScript and become enhanced with JS available.
// src/routes/contact/+page.server.ts
import type { Actions } from './$types';
import { fail, redirect } from '@sveltejs/kit';
export const actions: Actions = {
default: async ({ request }) => {
const data = await request.formData();
const email = data.get('email') as string;
const message = data.get('message') as string;
if (!email || !email.includes('@')) {
return fail(422, { email, message, error: 'Invalid email address' });
}
if (!message || message.length < 10) {
return fail(422, { email, message, error: 'Message too short' });
}
await sendEmail({ email, message });
redirect(303, '/contact/success');
}
};
<!-- src/routes/contact/+page.svelte -->
<script>
import { enhance } from '$app/forms';
let { form } = $props();
</script>
<form method="POST" use:enhance>
{#if form?.error}
<p class="error">{form.error}</p>
{/if}
<label>
Email
<input name="email" type="email" value={form?.email ?? ''} required />
</label>
<label>
Message
<textarea name="message">{form?.message ?? ''}</textarea>
</label>
<button type="submit">Send</button>
</form>
The use:enhance action from $app/forms intercepts the submission, sends it via fetch, and updates form without a full page reload β all with zero boilerplate.
API Routes
SvelteKitβs +server.ts files let you build REST endpoints within the same project.
// src/routes/api/search/+server.ts
import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async ({ url }) => {
const query = url.searchParams.get('q');
if (!query) {
error(400, 'Missing query parameter');
}
const results = await searchDatabase(query);
return json(results, {
headers: {
'Cache-Control': 'public, max-age=60'
}
});
};
export const POST: RequestHandler = async ({ request }) => {
const body = await request.json();
const result = await createItem(body);
return json(result, { status: 201 });
};
Deployment
Vercel
Vercel is the easiest deployment target for SvelteKit. Install the adapter and configure it:
npm install -D @sveltejs/adapter-vercel
// svelte.config.js
import adapter from '@sveltejs/adapter-vercel';
export default {
kit: {
adapter: adapter({
runtime: 'nodejs22.x',
// Optional: configure edge functions per route
})
}
};
Push to GitHub and import the repository in Vercel β zero additional configuration required. SvelteKitβs load functions become Vercel serverless functions automatically.
Cloudflare Pages
For globally distributed edge rendering, use the Cloudflare adapter:
npm install -D @sveltejs/adapter-cloudflare
// svelte.config.js
import adapter from '@sveltejs/adapter-cloudflare';
export default {
kit: {
adapter: adapter({
routes: {
include: ['/*'],
exclude: ['<all>']
}
})
}
};
Cloudflare runs your server-side code at the edge in Workers (V8 isolates), so avoid Node.js-specific APIs. Use platform.env to access Cloudflare bindings like KV, D1, and R2:
// src/routes/+page.server.ts
export const load = async ({ platform }) => {
const value = await platform?.env?.MY_KV?.get('key');
return { value };
};
Static Adapter
For fully static sites with no server-side logic:
npm install -D @sveltejs/adapter-static
// svelte.config.js
import adapter from '@sveltejs/adapter-static';
export default {
kit: {
adapter: adapter({
fallback: '404.html'
})
}
};
SvelteKit vs Next.js
| Feature | SvelteKit 5 | Next.js 15 |
|---|---|---|
| Reactivity model | Runes (compiler-based) | React hooks (runtime) |
| Bundle size | Minimal (no virtual DOM) | Larger (React runtime) |
| Routing | File-based (src/routes/) | File-based (app/ or pages/) |
| Data fetching | load functions | Server Components + fetch |
| Form handling | Form actions (built-in) | Server Actions |
| Edge support | Cloudflare adapter | Edge runtime |
| TypeScript | First-class | First-class |
| Learning curve | Low-to-medium | Medium-to-high |
| Ecosystem | Smaller but growing | Large and mature |
The biggest practical difference is mental model. Next.js 15 with React Server Components introduces a two-component-tree model that can be confusing. SvelteKitβs +page.server.ts / +page.svelte split is explicit and easy to reason about: server code in .server.ts, client-rendered UI in .svelte.
SvelteKit also compiles your components into efficient imperative DOM operations with no virtual DOM diffing at runtime. For content-heavy sites, this means faster Time to Interactive and smaller JavaScript payloads.
Getting Started
npm create svelte@latest my-app
cd my-app
npm install
npm run dev
The CLI will prompt you to choose TypeScript, ESLint, Prettier, Playwright, and Vitest β all recommended for production projects.
SvelteKit 5 with Svelte 5 runes is production-ready and represents a mature, opinionated full-stack framework. The explicit reactivity model of runes, combined with SvelteKitβs clean routing conventions and zero-config deployment adapters, makes it one of the most productive stacks available in 2026 β especially for teams that value performance and simplicity over ecosystem breadth.
50 CLI Commands That Save Hours Every Week
The exact Git shortcuts, file search tricks, Docker commands, and productivity hacks that senior developers use daily. Compiled from 5+ years of shipping production code.
- Git mastery (10 cmds)
- File & search wizardry
- Docker & DevOps tricks
- Process & network tools
- Productivity boosters
- macOS & Linux install guide
No spam. 1 email/week max. Unsubscribe anytime.
Found this guide useful? Check out our free developer tools.
Affiliate disclosure: Some links below are affiliate links β we may earn a small commission at no extra cost to you. Learn more.
Recommended Tools & Resources
DigitalOcean
$200 credit for new users. Simple, affordable cloud hosting for developers.
GitHub Student Pack
Free access to 100+ developer tools. Perfect for students and new devs.
Vercel
Deploy frontend apps instantly. Free tier is generous for side projects.
DevPlaybook Products
Boilerplates, scripts & AI toolkits to 10x your dev workflow.