The Complete SvelteKit Guide | Full Stack, Routing, Form Actions, Load, Hooks

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
  • load functions: 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

  • 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.