SvelteKit: The Full-Stack Framework for Svelte - Complete 2026 Guide
Master SvelteKit in 2026: file-based routing, SSR, form actions, load functions, and deployment. Learn why SvelteKit is the fastest way to build full-stack Svelte applications.
SvelteKit: The Full-Stack Framework for Svelte - Complete 2026 Guide
SvelteKit is the official full-stack framework for Svelte applications β the equivalent of what Next.js is to React or Nuxt is to Vue. It handles routing, server-side rendering, form handling, data loading, and deployment in a single cohesive package. If you know Svelte but havenβt tried SvelteKit yet, this guide will show you just how much it changes whatβs possible. If youβre new to both, youβre starting in exactly the right place.
What Makes SvelteKit Different?
Before diving into code, itβs worth understanding what SvelteKit is solving. Svelte is a compiler β it transforms declarative component code into tiny, framework-free JavaScript at build time. Thereβs no virtual DOM, no runtime framework code, just vanilla JavaScript that directly manipulates the DOM.
SvelteKit extends this with:
- File-based routing (like Next.js, but simpler)
- Multiple rendering modes: SSR, SSG, SPA, hybrid
- Server-side data loading with
loadfunctions - Server actions with progressive enhancement
- Adapter-based deployment to any platform
- Built-in TypeScript support
The result is a framework that produces smaller bundles, runs faster, and has a shallower learning curve than React-based alternatives.
Installation and Project Setup
# Create a new SvelteKit project
npm create svelte@latest my-app
# The CLI asks:
# - Template (skeleton, demo, or library)
# - TypeScript or JSDoc
# - ESLint, Prettier, Vitest, Playwright (optional)
cd my-app
npm install
npm run dev
Your project structure:
my-app/
βββ src/
β βββ routes/ # File-based routing (pages + API)
β β βββ +page.svelte # Homepage
β β βββ +layout.svelte # Root layout
β β βββ blog/
β β βββ +page.svelte
β β βββ [slug]/
β β βββ +page.svelte
β βββ lib/ # Shared utilities ($lib alias)
β β βββ components/
β β βββ server/ # Server-only code
β βββ app.html # HTML shell template
βββ static/ # Static assets
βββ svelte.config.js # SvelteKit configuration
βββ vite.config.js # Vite configuration
File-Based Routing
SvelteKitβs routing is entirely based on the file system. Every file in src/routes/ becomes a route.
Basic Routes
src/routes/
βββ +page.svelte β /
βββ about/
β βββ +page.svelte β /about
βββ blog/
β βββ +page.svelte β /blog
β βββ [slug]/
β βββ +page.svelte β /blog/:slug
βββ api/
βββ posts/
βββ +server.ts β /api/posts (API endpoint)
Route Files and Their Purposes
SvelteKit uses special filename conventions:
| File | Purpose |
|---|---|
+page.svelte | The page UI component |
+page.ts | Page load function (runs on server + client) |
+page.server.ts | Server-only load function and form actions |
+layout.svelte | Shared layout wrapping child routes |
+layout.ts | Layout load function |
+server.ts | API endpoint (GET, POST, PUT, DELETE) |
+error.svelte | Custom error page |
A Simple Page with Load Data
<!-- src/routes/blog/+page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<h1>Blog</h1>
<ul>
{#each data.posts as post}
<li>
<a href="/blog/{post.slug}">{post.title}</a>
<p>{post.description}</p>
</li>
{/each}
</ul>
// src/routes/blog/+page.server.ts
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async () => {
const posts = await db.post.findMany({
where: { published: true },
orderBy: { createdAt: 'desc' },
});
return { posts };
};
Data flows from load β data prop on the page. TypeScript types are auto-generated from the $types module β you get full type safety without writing types manually.
Dynamic Routes and Params
// src/routes/blog/[slug]/+page.server.ts
import type { PageServerLoad } from './$types';
import { error } from '@sveltejs/kit';
export const load: PageServerLoad = async ({ params }) => {
const post = await db.post.findUnique({
where: { slug: params.slug },
});
if (!post) {
error(404, 'Post not found');
}
return { post };
};
<!-- src/routes/blog/[slug]/+page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<article>
<h1>{data.post.title}</h1>
<div>{@html data.post.content}</div>
</article>
Optional and Rest Parameters
[param] β Required: /blog/my-post
[[param]] β Optional: /docs or /docs/intro
[...rest] β Catch-all: /files/a/b/c
(group) β Route groups (doesn't affect URL)
Form Actions: Progressive Enhancement Built In
SvelteKitβs form actions are one of its best features. They handle form submissions on the server, work without JavaScript, and enhance automatically with JS when available.
// src/routes/contact/+page.server.ts
import type { Actions, PageServerLoad } 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 || !message) {
return fail(400, {
error: 'Email and message are required',
email,
message
});
}
await sendEmail({ email, message });
redirect(303, '/contact/success');
}
};
<!-- src/routes/contact/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
import type { ActionData } from './$types';
export let form: ActionData;
</script>
<!-- use:enhance makes this work without JS reload -->
<form method="POST" use:enhance>
{#if form?.error}
<p class="error">{form.error}</p>
{/if}
<input
name="email"
type="email"
value={form?.email ?? ''}
required
/>
<textarea name="message" required>
{form?.message ?? ''}
</textarea>
<button type="submit">Send Message</button>
</form>
Without JavaScript: the form submits, the server processes it, the page reloads with the result.
With JavaScript (use:enhance): the form submits via fetch, the page updates without a full reload. Same server code, zero duplication.
API Routes (Server Endpoints)
For JSON APIs or non-form server interactions:
// src/routes/api/posts/+server.ts
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async ({ url }) => {
const limit = Number(url.searchParams.get('limit') ?? 10);
const posts = await db.post.findMany({
take: limit,
where: { published: true },
});
return json(posts);
};
export const POST: RequestHandler = async ({ request }) => {
const body = await request.json();
const post = await db.post.create({ data: body });
return json(post, { status: 201 });
};
Call it from the client or from load functions:
// From a load function
export const load: PageServerLoad = async ({ fetch }) => {
// Use the SvelteKit `fetch` - it handles cookies correctly
const posts = await fetch('/api/posts?limit=5').then(r => r.json());
return { posts };
};
Layouts and Navigation
Layouts wrap multiple routes with shared UI. Nested layouts work automatically:
<!-- src/routes/+layout.svelte -->
<script>
import Nav from '$lib/components/Nav.svelte';
import Footer from '$lib/components/Footer.svelte';
</script>
<Nav />
<main>
<slot /> <!-- Child pages render here -->
</main>
<Footer />
<!-- src/routes/blog/+layout.svelte -->
<script>
// This layout only wraps /blog/* routes
</script>
<aside>
<BlogSidebar />
</aside>
<div class="content">
<slot />
</div>
Programmatic Navigation
<script>
import { goto, invalidate, invalidateAll } from '$app/navigation';
import { page } from '$app/stores';
// Current URL, params, route info
$: currentPath = $page.url.pathname;
$: params = $page.params;
function navigate() {
goto('/dashboard', { replaceState: false });
}
// Refresh load data without full navigation
function refresh() {
invalidateAll();
}
</script>
Authentication Pattern
SvelteKit doesnβt prescribe an auth solution, but the hooks pattern makes it clean:
// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
// Run on every request
const sessionToken = event.cookies.get('session');
if (sessionToken) {
const user = await validateSession(sessionToken);
event.locals.user = user;
}
return resolve(event);
};
// src/routes/dashboard/+page.server.ts
import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ locals }) => {
if (!locals.user) {
redirect(302, '/login');
}
const dashboardData = await getDashboardData(locals.user.id);
return { user: locals.user, ...dashboardData };
};
SvelteKit vs Next.js
Hereβs the honest comparison for 2026:
| Feature | SvelteKit | Next.js |
|---|---|---|
| Bundle size | Very small (no framework runtime) | Larger (React runtime) |
| Learning curve | Gentle | Steeper (React concepts) |
| TypeScript support | First-class | First-class |
| SSR | Yes | Yes |
| SSG | Yes | Yes |
| Server actions | Form Actions (built-in) | Server Actions (React 19) |
| API routes | +server.ts | route.ts |
| Deployment | Adapters (any platform) | Vercel-optimized |
| Community | Growing | Very large |
| Job market | Smaller | Much larger |
| Performance | Excellent | Good |
Choose SvelteKit when:
- Bundle size and runtime performance matter
- You want simpler, less verbose component code
- Youβre building a new project without existing React investment
- You want progressive enhancement built in, not bolted on
Choose Next.js when:
- Team already knows React
- Ecosystem/library compatibility is critical
- You need the largest possible community for support
Deployment with Adapters
SvelteKitβs adapter system means it runs anywhere:
# Vercel (auto-detected)
npm install @sveltejs/adapter-vercel
# Netlify
npm install @sveltejs/adapter-netlify
# Node.js server
npm install @sveltejs/adapter-node
# Static site (no server)
npm install @sveltejs/adapter-static
# Cloudflare Pages/Workers
npm install @sveltejs/adapter-cloudflare
// svelte.config.js
import adapter from '@sveltejs/adapter-vercel';
export default {
kit: {
adapter: adapter({
runtime: 'edge', // or 'nodejs20.x'
})
}
};
For fully static sites:
import adapter from '@sveltejs/adapter-static';
export default {
kit: {
adapter: adapter({
pages: 'build',
assets: 'build',
fallback: undefined, // '200.html' for SPA mode
})
}
};
Environment Variables
// Server-only secrets: $env/static/private
import { DATABASE_URL } from '$env/static/private';
// Public env vars: $env/static/public
import { PUBLIC_API_URL } from '$env/static/public';
// Dynamic (runtime) env vars
import { env } from '$env/dynamic/private';
const dbUrl = env.DATABASE_URL;
SvelteKit will throw a build error if you accidentally import private env vars in client code β a much safer default than most frameworks.
Testing SvelteKit Apps
# Unit testing with Vitest
npm install -D vitest @testing-library/svelte
# E2E with Playwright
npm install -D @playwright/test
// Component test example
import { render, screen } from '@testing-library/svelte';
import BlogCard from '$lib/components/BlogCard.svelte';
test('renders post title', () => {
render(BlogCard, {
props: { title: 'My Post', description: 'Post description' }
});
expect(screen.getByText('My Post')).toBeInTheDocument();
});
Production Checklist
Before deploying a SvelteKit app to production:
- Set
ORIGINenv var for CSRF protection - Configure CSP headers in hooks
- Add error boundaries with
+error.svelte - Use
preloadlink headers for critical resources - Configure caching headers in the adapter
- Set up proper logging in
hooks.server.ts - Add rate limiting to API routes and form actions
- Test forms without JavaScript to verify progressive enhancement
Conclusion
SvelteKit is one of the most thoughtfully designed full-stack frameworks available in 2026. The file-based routing is clean, form actions solve the data mutation problem elegantly, and the adapter system means youβre never locked into one deployment platform.
The productivity gains are real: less boilerplate than React-based stacks, built-in progressive enhancement, and Svelteβs compiler-first approach keeping bundle sizes small. If youβre evaluating frameworks for a new project, SvelteKit deserves serious consideration β especially if youβre building something that needs to work well without JavaScript or wants performance without effort.
Resources:
- Official docs: kit.svelte.dev
- Tutorial: learn.svelte.dev
- Examples: github.com/sveltejs/kit/tree/main/examples
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.