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.
.claude/skills/davila7-sveltekit/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 136% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 77% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 68% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 100% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 468% | 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 CSS| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 7,714 | 5,489 | -29% | 1 | 1 | 0% | 1,543 | 3,644 | +136% | 0 | 0 | — |
case-02 | fail→pass | 12,910 | 10,316 | -20% | 1 | 1 | 0% | 2,771 | 4,915 | +77% | 0 | 0 | — |
case-03 | fail→pass | 13,243 | 12,416 | -6% | 1 | 1 | 0% | 3,077 | 5,166 | +68% | 0 | 0 | — |
case-04 | pass→pass | 7,402 | 4,674 | -37% | 1 | 1 | 0% | 1,688 | 3,370 | +100% | 0 | 0 | — |
case-05 | pass→pass | 2,938 | 2,684 | -9% | 1 | 1 | 0% | 515 | 2,924 | +468% | 0 | 0 | — |
case-10 | pass→pass | 3,328 | 3,972 | +19% | 1 | 1 | 0% | 639 | 3,011 | +371% | 0 | 0 | — |
case-06 | pass→pass | 4,834 | 4,931 | +2% | 1 | 1 | 0% | 911 | 3,240 | +256% | 0 | 0 | — |
case-07 | pass→pass | 4,874 | 2,892 | -41% | 1 | 1 | 0% | 944 | 2,890 | +206% | 0 | 0 | — |
case-08 | pass→pass | 7,315 | 4,134 | -43% | 1 | 1 | 0% | 1,169 | 3,090 | +164% | 0 | 0 | — |
case-09 | pass→pass | 5,175 | 3,564 | -31% | 1 | 1 | 0% | 928 | 3,102 | +234% | 0 | 0 | — |
case-11 | pass→pass | 6,655 | 3,380 | -49% | 1 | 1 | 0% | 1,303 | 3,039 | +133% | 0 | 0 | — |
case-12 | pass→pass | 4,094 | 2,813 | -31% | 1 | 1 | 0% | 774 | 2,877 | +272% | 0 | 0 | — |
case-13 | pass→pass | 13,726 | 9,727 | -29% | 1 | 1 | 0% | 2,570 | 4,336 | +69% | 0 | 0 | — |
case-14 | pass→pass | 3,262 | 3,476 | +7% | 1 | 1 | 0% | 624 | 3,043 | +388% | 0 | 0 | — |
case-15 | pass→pass | 6,462 | 4,647 | -28% | 1 | 1 | 0% | 1,396 | 3,335 | +139% | 0 | 0 | — |
case-16 | pass→pass | 3,886 | 3,258 | -16% | 1 | 1 | 0% | 637 | 3,065 | +381% | 0 | 0 | — |
case-17 | pass→pass | 5,368 | 3,445 | -36% | 1 | 1 | 0% | 1,014 | 3,055 | +201% | 0 | 0 | — |
case-18 | pass→pass | 5,334 | 3,914 | -27% | 1 | 1 | 0% | 1,166 | 3,238 | +178% | 0 | 0 | — |
case-19 | pass→pass | 5,514 | 3,756 | -32% | 1 | 1 | 0% | 1,116 | 3,158 | +183% | 0 | 0 | — |
case-20 | pass→pass | 3,313 | 3,335 | +1% | 1 | 1 | 0% | 648 | 3,029 | +367% | 0 | 0 | — |
case-21 | pass→pass | 3,183 | 2,893 | -9% | 1 | 1 | 0% | 632 | 3,030 | +379% | 0 | 0 | — |
case-22 | pass→pass | 4,728 | 5,863 | +24% | 1 | 1 | 0% | 893 | 3,460 | +287% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +14 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.