Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Build full-stack web applications with SvelteKit — file-based routing, SSR, SSG, API routes, and form actions in one framework.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-12 | ✗→✓ | ▲ Improved | 487% | 0% |
| case-11 | ✓→✗ | ▼ Worse | 271% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 124% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 324% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 628% | 0% |
SvelteKit is the official full-stack framework built on top of Svelte. It provides file-based routing, server-side rendering (SSR), static site generation (SSG), API routes, and progressive form actions — all with Svelte's compile-time reactivity model that ships zero runtime overhead to the browser. Use this skill when building fast, modern web apps where both DX and performance matter.
+page.svelte, +layout.svelte, load functions, or form actionsbashnpm create svelte@latest my-app cd my-app npm install npm run dev
Choose Skeleton project + TypeScript + ESLint/Prettier when prompted.
Directory structure after scaffolding:
src/
routes/
+page.svelte ← Root page component
+layout.svelte ← Root layout (wraps all pages)
+error.svelte ← Error boundary
lib/
server/ ← Server-only code (never bundled to client)
components/ ← Shared components
app.html ← HTML shell
static/ ← Static assetsEvery +page.svelte file in src/routes/ maps directly to a URL:
src/routes/+page.svelte → /
src/routes/about/+page.svelte → /about
src/routes/blog/[slug]/+page.svelte → /blog/:slug
src/routes/shop/[...path]/+page.svelte → /shop/* (catch-all)Route groups (no URL segment): wrap in (group)/ folder. Private routes (not accessible as URLs): prefix with _ or (group).
load FunctionsUse a +page.ts (universal) or +page.server.ts (server-only) file alongside the page:
typescript// src/routes/blog/[slug]/+page.server.ts import { error } from '@sveltejs/kit'; import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ params, fetch }) => { const post = await fetch(`/api/posts/${params.slug}`).then(r => r.json()); if (!post) { error(404, 'Post not found'); } return { post }; };
svelte<!-- src/routes/blog/[slug]/+page.svelte --> <script lang="ts"> import type { PageData } from './$types'; export let data: PageData; </script> <h1>{data.post.title}</h1> <article>{@html data.post.content}</article>
Create +server.ts files for REST-style endpoints:
typescript// 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 }); 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 }); };
Form actions are the SvelteKit-native way to handle mutations — no client-side fetch required:
typescript// src/routes/contact/+page.server.ts import { fail, redirect } from '@sveltejs/kit'; import type { Actions } from './$types'; export const actions: Actions = { default: async ({ request }) => { const data = await request.formData(); const email = data.get('email'); if (!email) { return fail(400, { email, missing: true }); } await sendEmail(String(email)); redirect(303, '/thank-you'); } };
svelte<!-- src/routes/contact/+page.svelte --> <script lang="ts"> import { enhance } from '$app/forms'; import type { ActionData } from './$types'; export let form: ActionData; </script> <form method="POST" use:enhance> <input name="email" type="email" /> {#if form?.missing}<p class="error">Email is required</p>{/if} <button type="submit">Subscribe</button> </form>
svelte<!-- src/routes/+layout.svelte --> <script lang="ts"> import type { LayoutData } from './$types'; export let data: LayoutData; </script> <nav> <a href="/">Home</a> <a href="/blog">Blog</a> {#if data.user} <a href="/dashboard">Dashboard</a> {/if} </nav> <slot /> <!-- child page renders here -->
typescript// src/routes/+layout.server.ts import type { LayoutServerLoad } from './$types'; export const load: LayoutServerLoad = async ({ locals }) => { return { user: locals.user ?? null }; };
Control per-route rendering with page options:
typescript// src/routes/docs/+page.ts export const prerender = true; // Static — generated at build time export const ssr = true; // Default — rendered on server per request export const csr = false; // Disable client-side hydration entirely
typescript// src/routes/dashboard/+layout.server.ts import { redirect } from '@sveltejs/kit'; import type { LayoutServerLoad } from './$types'; export const load: LayoutServerLoad = async ({ locals }) => { if (!locals.user) { redirect(303, '/login'); } return { user: locals.user }; };
typescript// src/hooks.server.ts import type { Handle } from '@sveltejs/kit'; import { verifyToken } from '$lib/server/auth'; export const handle: Handle = async ({ event, resolve }) => { const token = event.cookies.get('session'); if (token) { event.locals.user = await verifyToken(token); } return resolve(event); };
svelte<script lang="ts"> import { invalidateAll } from '$app/navigation'; async function refresh() { await invalidateAll(); // re-runs all load functions on the page } </script> <button on:click={refresh}>Refresh</button>
+page.server.ts for database/auth logic — it never ships to the client$lib/server/ for shared server-only modules (DB client, auth helpers)fetch — works without JSload return values with generated $types (PageData, LayoutData)event.locals in hooks to pass server-side context to load functions+page.svelte or +layout.svelte directlylocals on the serveruse:enhance on forms — without it, forms lose progressive enhancement+page.server.ts, +server.ts, and $lib/server/ runs exclusively on the server — safe for DB queries, secrets, and session validation.error(403) or redirect(303) from @sveltejs/kit rather than returning raw error objects.httpOnly: true and secure: true on all auth cookies.checkOrigin in production.Cannot use import statement in a module in +page.server.tsSolution: The file must be .ts or .js, not .svelte. Server files and Svelte components are separate.
undefined on first SSR renderSolution: Populate the store from the load function return value (data prop), not from client-side onMount.
Solution: Use redirect(303, '/path') from @sveltejs/kit, not a plain return. 303 is required for POST redirects.
locals.user is undefined inside a +page.server.ts load functionSolution: Set event.locals.user in src/hooks.server.ts before the resolve() call.
@nextjs-app-router-patterns — When you prefer React over Svelte for SSR/SSG@trpc-fullstack — Add end-to-end type safety to SvelteKit API routes@auth-implementation-patterns — Authentication patterns usable with SvelteKit hooks@tailwind-patterns — Styling SvelteKit apps with Tailwind CSSOther measured skills in the registry, with their headline benchmark lift.