The Complete SvelteKit Guide | Full Stack, Routing, Form Actions, Load, Hooks
What this post covers
This is a complete guide to building full-stack web apps with SvelteKit. It walks through file-based routing, load functions, Form Actions, Hooks, and deployment—with practical examples.
From the field: While migrating a Next.js project to SvelteKit, we cut bundle size by about 60% and doubled initial load performance—here is what we learned.
Introduction: “Next.js feels too heavy”
Real-world scenarios
Scenario 1: getServerSideProps gets unwieldy
Data-fetching logic is split across layers. SvelteKit keeps it simple with load functions.
Scenario 2: The bundle is large
The React bundle is around 150KB. Svelte is about 20KB. Scenario 3: Forms are tedious
You end up maintaining separate API routes. SvelteKit simplifies this with Form Actions.
1. What is SvelteKit?
Core traits
SvelteKit is a full-stack framework built on Svelte. Main advantages:
- Small bundle: ~20KB (React is often ~150KB)
- Fast runtime: compile-time optimizations
- Minimal syntax: little boilerplate
- Full stack: SSR and APIs built in
- File-based routing: easy to navigate
2. Creating a project
Below is a short bash example. Run it locally to see how it behaves.
npm create svelte@latest my-app
cd my-app
npm install
npm run dev
3. File-based routing
Structure
This layout shows how routes map to files. Read it alongside the tree to see each piece’s role.
src/routes/
├── +page.svelte # /
├── about/
│ └── +page.svelte # /about
├── blog/
│ ├── +page.svelte # /blog
│ └── [slug]/
│ └── +page.svelte # /blog/:slug
└── api/
└── users/
└── +server.ts # /api/users
4. Load functions
+page.ts
This TypeScript example shows a typical load. It uses async/await for efficient data fetching—run it to verify the flow.
// src/routes/blog/+page.ts
export async function load({ fetch }) {
const response = await fetch('/api/posts');
const posts = await response.json();
return {
posts,
};
}
The Svelte page below consumes the data returned by load. Follow how data flows into the template.
<!-- src/routes/blog/+page.svelte -->
<script lang="ts">
export let data;
</script>
<h1>Blog</h1>
<ul>
{#each data.posts as post}
<li>
<a href="/blog/{post.slug}">{post.title}</a>
</li>
{/each}
</ul>
+page.server.ts
This server-only load imports what it needs, uses async/await, handles errors for stability, and branches with conditionals. Step through each part to see how it fits together.
// src/routes/blog/[slug]/+page.server.ts
import { error } from '@sveltejs/kit';
export async function load({ params }) {
const post = await db.post.findUnique({
where: { slug: params.slug },
});
if (!post) {
throw error(404, 'Post not found');
}
return { post };
}
5. Form Actions
+page.server.ts
This detailed TypeScript example defines Form Actions: imports, async work, validation-style failures, and a redirect on success. Read it line by line to see the flow.
// src/routes/login/+page.server.ts
import { fail, redirect } from '@sveltejs/kit';
export const actions = {
default: async ({ request, cookies }) => {
const data = await request.formData();
const email = data.get('email');
const password = data.get('password');
if (!email || !password) {
return fail(400, { email, missing: true });
}
const user = await authenticateUser(email, password);
if (!user) {
return fail(401, { email, incorrect: true });
}
cookies.set('session', user.sessionId, { path: '/' });
throw redirect(303, '/dashboard');
},
};
+page.svelte
This Svelte form uses progressive enhancement with enhance and surfaces server-returned errors. Each block handles a specific validation outcome.
<!-- src/routes/login/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
export let form;
</script>
<form method="POST" use:enhance>
<input name="email" type="email" value={form?.email ?? ''} required />
<input name="password" type="password" required />
{#if form?.missing}
<p class="error">Email and password are required</p>
{/if}
{#if form?.incorrect}
<p class="error">Invalid credentials</p>
{/if}
<button type="submit">Login</button>
</form>
6. API routes
// src/routes/api/users/+server.ts
import { json } from '@sveltejs/kit';
export async function GET() {
const users = await db.user.findMany();
return json(users);
}
export async function POST({ request }) {
const data = await request.json();
const user = await db.user.create({
data: {
name: data.name,
email: data.email,
},
});
return json(user, { status: 201 });
}
7. Hooks
hooks.server.ts
This TypeScript hook imports Handle, runs async work per request, and branches on whether a session cookie exists. See how event.locals is populated before resolve.
// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
const session = event.cookies.get('session');
if (session) {
event.locals.user = await getUserFromSession(session);
}
return resolve(event);
};
Usage
A server load can enforce auth using locals set in the hook: async flow, redirect when unauthenticated, return user data when allowed.
// src/routes/dashboard/+page.server.ts
import { redirect } from '@sveltejs/kit';
// Example run
export async function load({ locals }) {
if (!locals.user) {
throw redirect(303, '/login');
}
return {
user: locals.user,
};
}
8. Deployment
Vercel
npm install -D @sveltejs/adapter-vercel
Configure the Vercel adapter in svelte.config.js as shown below.
// svelte.config.js
import adapter from '@sveltejs/adapter-vercel';
export default {
kit: {
adapter: adapter(),
},
};
Cloudflare Pages
npm install -D @sveltejs/adapter-cloudflare
Node.js
npm install -D @sveltejs/adapter-node
Summary and checklist
Key takeaways
- SvelteKit: full-stack framework for Svelte
- Small bundle: ~20KB
loadfunctions: data fetching for pages- Form Actions: simpler form handling
- Hooks: cross-cutting request logic
- File-based routing: intuitive structure
Implementation checklist
- Create a SvelteKit project
- Implement routing
- Fetch data with
load - Add Form Actions
- Write API routes
- Configure Hooks
- Deploy
Related reading
- The Complete Svelte 5 Guide
- The Complete Remix Guide
- Next.js App Router Guide
Keywords in this post
SvelteKit, Svelte, Full Stack, SSR, Web Framework, JavaScript
Frequently asked questions (FAQ)
Q. SvelteKit vs Next.js—which should I pick?
A. SvelteKit tends to be lighter and faster. Next.js has the larger ecosystem. Prefer SvelteKit when raw performance matters; choose Next.js when ecosystem breadth is the priority.
Q. Do I need to know Svelte first?
A. Yes—SvelteKit is built on Svelte. That said, Svelte has a gentle learning curve.
Q. Is it production-ready?
A. Yes. SvelteKit 1.0 shipped and the framework is stable for real workloads.
Q. Does it support TypeScript?
A. Yes—first-class TypeScript support throughout.