SvelteKit hat sich 2026 als das schnellste und produktivste Full-Stack-Framework im JavaScript-Ökosystem etabliert. In Kombination mit Claude Code entsteht ein Workflow, der TypeScript-Entwicklung dramatisch beschleunigt: von der Projektstruktur über typisierte Load Functions bis hin zu progressiv enhancten Form Actions. Dieser Artikel zeigt, wie du SvelteKit-Projekte mit Claude Code effizient entwickelst — mit echten TypeScript-Beispielen für jede Schicht der Anwendung.
Was du lernst: SvelteKit-Routing, Svelte 5 Runes, Load Functions, Form Actions, Hooks & Middleware, Adapter-Deployment — alles typsicher mit TypeScript und KI-Unterstützung durch Claude Code.
1. SvelteKit Setup & Routing
SvelteKit nutzt ein dateibasiertes Routing-System, das sich direkt aus der Verzeichnisstruktur unter src/routes/ ableitet. Jede Route besteht aus klar definierten Dateitypen: +page.svelte für die UI-Komponente, +layout.svelte für geteilte Layouts, +page.ts oder +page.server.ts für Load Functions sowie +server.ts für API-Endpoints.
ROUTING
Projektstruktur mit Claude Code erstellen
Claude Code versteht SvelteKit-Konventionen und kann vollständige Routing-Strukturen generieren. Prompt-Beispiel:
# Prompt an Claude Code:
"Erstelle eine SvelteKit TypeScript App mit:
- Öffentliche Routen: /, /blog, /blog/[slug]
- Auth-geschützte Routen: /dashboard, /dashboard/settings
- Route Group (marketing) für shared Layout
- API-Endpoint /api/posts"
Claude generiert die korrekte Verzeichnisstruktur:
src/routes/
├── (marketing)/
│ ├── +layout.svelte # Shared marketing layout
│ ├── +page.svelte # Startseite
│ └── blog/
│ ├── +page.svelte # Blog-Übersicht
│ ├── +page.ts # Load Function (client + server)
│ └── [slug]/
│ ├── +page.svelte # Blog-Detailseite
│ └── +page.server.ts # Server Load Function
├── dashboard/
│ ├── +layout.server.ts # Auth-Check für alle /dashboard Routen
│ ├── +layout.svelte
│ ├── +page.svelte
│ └── settings/
│ └── +page.svelte
└── api/
└── posts/
└── +server.ts # REST API Endpoint
+page.svelte — Grundstruktur mit TypeScript
<!-- src/routes/(marketing)/blog/+page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
let { data }: { data: PageData } = $props();
</script>
<svelte:head>
<title>{data.title} | Mein Blog</title>
<meta name="description" content={data.description} />
</svelte:head>
<main>
<h1>Blog</h1>
{#each data.posts as post (post.id)}
<article>
<h2><a href="/blog/{post.slug}">{post.title}</a></h2>
<p>{post.excerpt}</p>
</article>
{/each}
</main>
+layout.svelte mit Slot und Navigation
<!-- src/routes/(marketing)/+layout.svelte -->
<script lang="ts">
import type { LayoutData } from './$types';
import Nav from '$lib/components/Nav.svelte';
import Footer from '$lib/components/Footer.svelte';
let { data, children }: { data: LayoutData; children: Snippet } = $props();
</script>
<Nav user={data.user} />
<main class="container">
{@render children()}
</main>
<Footer />
Dynamische Routen mit [slug]
<!-- src/routes/(marketing)/blog/[slug]/+page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
let { data }: { data: PageData } = $props();
</script>
<article class="prose">
<h1>{data.post.title}</h1>
<time>{data.post.publishedAt}</time>
<div class="content">{@html data.post.content}</div>
</article>
Claude Code Tipp: Gib Claude den vollständigen Pfad und den gewünschten Typ (+page.svelte, +page.server.ts etc.) im Prompt an. Claude kennt alle SvelteKit-Konventionen und generiert korrekte TypeScript-Typen aus ./$types automatisch.
Route Groups und API-Endpoints
Route Groups mit (groupname)-Syntax erlauben gemeinsame Layouts ohne URL-Einfluss. API-Endpoints via +server.ts unterstützen alle HTTP-Methoden typsicher:
// src/routes/api/posts/+server.ts
import type { RequestHandler } from './$types';
import { json } from '@sveltejs/kit';
import { db } from '$lib/server/db';
export const GET: RequestHandler = async ({ url }) => {
const limit = Number(url.searchParams.get('limit') ?? '10');
const posts = await db.posts.findMany({ take: limit });
return json(posts);
};
export const POST: RequestHandler = async ({ request }) => {
const body = await request.json();
const post = await db.posts.create({ data: body });
return json(post, { status: 201 });
};
2. Svelte 5 Runes & Reaktivität
Svelte 5 führt Runes als explizites reaktives Primitiv-System ein. Anstatt impliziter Reaktivität durch deklarierte Variablen verwendet Svelte 5 explizite Runes-Aufrufe: $state, $derived, $effect und $props. Das macht den Code lesbarer, vorhersehbarer — und für Claude Code erheblich einfacher zu generieren und zu debuggen.
SVELTE 5
Runes vs. klassische Stores im Vergleich
| Feature |
Svelte 4 (alt) |
Svelte 5 Runes |
| Reaktiver State |
let count = 0 (implizit) |
let count = $state(0) |
| Abgeleiteter Wert |
$: doubled = count * 2 |
let doubled = $derived(count * 2) |
| Side Effects |
$: { effect() } |
$effect(() => { effect() }) |
| Props |
export let name |
let { name } = $props() |
| TypeScript |
Umständlich |
Vollständig typsicher |
$state — Reaktiver State mit TypeScript Generics
<!-- src/lib/components/Counter.svelte -->
<script lang="ts">
// Einfacher State
let count = $state(0);
// Typisierter State mit Interface
interface User {
id: string;
name: string;
email: string;
}
let user = $state<User | null>(null);
// Array State — reaktiv auf push/splice etc.
let items = $state<string[]>([]);
function addItem(item: string) {
items.push(item); // Direkte Mutation — Svelte 5 trackt das!
}
</script>
<button onclick={() => count++}>Clicks: {count}</button>
$derived — Berechnete Werte
<script lang="ts">
let firstName = $state('');
let lastName = $state('');
// Einfache Ableitung
let fullName = $derived(`${firstName} ${lastName}`.trim());
// Komplexe Ableitung mit $derived.by()
let cartTotal = $derived.by(() => {
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
});
// TypeScript Generics in derived
let filteredPosts = $derived<Post[]>(
posts.filter(p => p.published && p.category === selectedCategory)
);
</script>
$effect — Reaktive Seiteneffekte
<script lang="ts">
let searchQuery = $state('');
let results = $state<SearchResult[]>([]);
let loading = $state(false);
// Läuft wenn searchQuery sich ändert
$effect(async () => {
if (!searchQuery) { results = []; return; }
loading = true;
const res = await fetch(`/api/search?q=${searchQuery}`);
results = await res.json();
loading = false;
});
// Cleanup-Funktion für Event Listener
$effect(() => {
const handler = (e: KeyboardEvent) => {
if (e.key === 'Escape') searchQuery = '';
};
document.addEventListener('keydown', handler);
return () => document.removeEventListener('keydown', handler);
});
</script>
$props — Typisierte Komponenten-Props
<!-- src/lib/components/Button.svelte -->
<script lang="ts">
import type { Snippet } from 'svelte';
interface Props {
variant?: 'primary' | 'secondary' | 'ghost';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
onclick?: () => void;
children: Snippet;
}
let {
variant = 'primary',
size = 'md',
disabled = false,
onclick,
children
}: Props = $props();
</script>
<button
class="btn btn-{variant} btn-{size}"
{disabled}
{onclick}
>
{@render children()}
</button>
Claude Code Tipp: Beschreibe die gewünschte Komponenten-API mit Props-Interface und Slot-Struktur. Claude generiert vollständig typsichere Svelte-5-Komponenten mit korrekten Runes-Aufrufen und Snippet-Typen.
3. Load Functions & Data Fetching
Load Functions sind das Herzstück des SvelteKit-Datenflusses. Sie laufen vor dem Rendering der Seite, entweder nur auf dem Server (+page.server.ts), auf Client und Server (+page.ts) oder im Layout (+layout.server.ts). Das TypeScript-Typsystem von SvelteKit stellt sicher, dass data in +page.svelte immer den korrekten Typ aus der Load Function hat.
LOAD
+page.server.ts — Server-only Load Function
// src/routes/(marketing)/blog/[slug]/+page.server.ts
import type { PageServerLoad } from './$types';
import { error } from '@sveltejs/kit';
import { db } from '$lib/server/db';
export const load: PageServerLoad = async ({ params, locals }) => {
// params.slug ist typsicher durch SvelteKit
const post = await db.posts.findUnique({
where: { slug: params.slug, published: true },
include: { author: true, tags: true }
});
if (!post) {
throw error(404, { message: 'Artikel nicht gefunden' });
}
// Nur serialisierbare Daten zurückgeben
return {
post,
relatedPosts: await db.posts.findMany({
where: { tags: { some: { id: { in: post.tags.map(t => t.id) } } } },
take: 3
}),
isLoggedIn: !!locals.user
};
};
+page.ts — Universelle Load Function (Client + Server)
// src/routes/(marketing)/blog/+page.ts
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ fetch, url }) => {
// fetch ist ein SvelteKit-patch von fetch — SSR-sicher
const page = Number(url.searchParams.get('page') ?? '1');
const [posts, categories] = await Promise.all([
fetch(`/api/posts?page=${page}&limit=10`).then(r => r.json()),
fetch('/api/categories').then(r => r.json())
]);
return {
posts,
categories,
currentPage: page,
title: 'Blog',
description: 'Alle Artikel zu KI-Entwicklung'
};
};
LayoutServerLoad — Globale Auth-Daten
// src/routes/+layout.server.ts
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async ({ locals }) => {
// locals.user wird in hooks.server.ts gesetzt
return {
user: locals.user ?? null
};
};
parent() und depends() für Cache-Invalidierung
// src/routes/dashboard/+page.server.ts
import type { PageServerLoad } from './$types';
import { redirect } from '@sveltejs/kit';
export const load: PageServerLoad = async ({ parent, depends }) => {
// Daten aus dem übergeordneten Layout laden
const { user } = await parent();
if (!user) {
throw redirect(303, '/login');
}
// Cache-Key für invalidate() im Client
depends('app:dashboard');
return {
stats: await fetchDashboardStats(user.id),
recentActivity: await fetchActivity(user.id, { limit: 10 })
};
};
Claude Code Tipp: Zeige Claude eine bestehende Load Function und frage nach Erweiterungen wie Caching, Fehlerbehandlung oder Parallel-Fetching. Claude ergänzt den Code korrekt und behält die TypeScript-Typen bei.
4. Form Actions & Progressive Enhancement
Form Actions sind SvelteKits Antwort auf die Frage: Wie verarbeitet man Formulardaten serverseitig — ohne API-Layer, ohne CORS, ohne manuelle Fehlerbehandlung? Mit use:enhance werden Forms progressiv verbessert: Sie funktionieren ohne JavaScript (Plain HTML Form Submit) und werden mit JS zu einer SPA-Experience. Claude Code kann komplexe Form-Action-Patterns mit superforms-Integration generieren.
ACTIONS
Default und Named Actions
// src/routes/contact/+page.server.ts
import type { Actions, PageServerLoad } from './$types';
import { fail, redirect } from '@sveltejs/kit';
import { z } from 'zod';
const contactSchema = z.object({
name: z.string().min(2, 'Name zu kurz'),
email: z.string().email('Ungültige E-Mail'),
message: z.string().min(10, 'Nachricht zu kurz')
});
export const actions: Actions = {
// Default Action: ?/default oder einfach POST
default: async ({ request }) => {
const formData = await request.formData();
const raw = Object.fromEntries(formData);
const result = contactSchema.safeParse(raw);
if (!result.success) {
return fail(400, {
errors: result.error.flatten().fieldErrors,
values: raw
});
}
await sendEmail(result.data);
throw redirect(303, '/contact/success');
},
// Named Action: ?/subscribe
subscribe: async ({ request }) => {
const formData = await request.formData();
const email = formData.get('email') as string;
if (!email) return fail(400, { emailError: 'E-Mail erforderlich' });
await addToNewsletter(email);
return { success: true, message: 'Erfolgreich angemeldet!' };
}
};
use:enhance — Progressive Enhancement im Template
<!-- src/routes/contact/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
import type { ActionData } from './$types';
let { form }: { form: ActionData } = $props();
let submitting = $state(false);
</script>
<form
method="POST"
use:enhance={({ formElement, formData, action, cancel }) => {
submitting = true;
// Optionaler Custom-Handler vor Submit
return async ({ result, update }) => {
submitting = false;
if (result.type === 'success') {
formElement.reset();
}
await update();
};
}}
>
<label>
Name
<input name="name" value={form?.values?.name ?? ''} />
{#if form?.errors?.name}
<span class="error">{form.errors.name[0]}</span>
{/if}
</label>
<button type="submit" disabled={submitting}>
{submitting ? 'Senden...' : 'Absenden'}
</button>
</form>
Superforms Integration für komplexe Formulare
// src/routes/dashboard/profile/+page.server.ts
import { superValidate, fail } from 'sveltekit-superforms';
import { zod } from 'sveltekit-superforms/adapters';
import { z } from 'zod';
const profileSchema = z.object({
displayName: z.string().min(2).max(50),
bio: z.string().max(500).optional(),
website: z.string().url().optional()
});
export const load: PageServerLoad = async ({ locals }) => {
const form = await superValidate(locals.user, zod(profileSchema));
return { form };
};
export const actions: Actions = {
default: async ({ request, locals }) => {
const form = await superValidate(request, zod(profileSchema));
if (!form.valid) return fail(400, { form });
await db.users.update({
where: { id: locals.user.id },
data: form.data
});
return { form };
}
};
5. Hooks & Middleware
SvelteKit Hooks erlauben Middleware-Logik auf Server-Ebene: Authentifizierung, Logging, CORS-Header, Locale-Detection und mehr. Die Hooks laufen vor jeder Request-Verarbeitung und haben Zugriff auf das event-Objekt mit Request, Cookies, Locals und URL.
HOOKS
hooks.server.ts — handle, handleError, handleFetch
// src/hooks.server.ts
import type { Handle, HandleError, HandleFetch } from '@sveltejs/kit';
import { sequence } from '@sveltejs/kit/hooks';
import { db } from '$lib/server/db';
import { verifyToken } from '$lib/server/auth';
// Authentifizierungs-Hook
const authHook: Handle = async ({ event, resolve }) => {
const token = event.cookies.get('session_token');
if (token) {
try {
const payload = verifyToken(token);
event.locals.user = await db.users.findUnique({
where: { id: payload.userId }
});
} catch {
event.cookies.delete('session_token', { path: '/' });
}
}
return resolve(event);
};
// Security-Headers-Hook
const securityHook: Handle = async ({ event, resolve }) => {
const response = await resolve(event);
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
return response;
};
// Mehrere Hooks mit sequence() verketten
export const handle: Handle = sequence(authHook, securityHook);
handleError — Globale Fehlerbehandlung
// src/hooks.server.ts (Fortsetzung)
export const handleError: HandleError = async ({ error, event, status, message }) => {
const errorId = crypto.randomUUID();
// In Logging-Service schreiben
console.error({
errorId,
status,
message,
path: event.url.pathname,
error: error instanceof Error ? error.stack : error
});
// Sentry, Datadog etc. hier integrieren
if (status >= 500) {
await notifySlack({ errorId, message, path: event.url.pathname });
}
// Rückgabe erscheint in $page.error
return {
message: status === 404 ? 'Seite nicht gefunden' : 'Ein Fehler ist aufgetreten',
errorId
};
};
handleFetch — Outgoing Requests modifizieren
// Nützlich für: Auth-Header zu internen APIs, Request-Logging, Caching
export const handleFetch: HandleFetch = async ({ request, fetch, event }) => {
// Interne API-Calls während SSR mit Auth-Token versehen
if (request.url.startsWith('http://localhost')) {
const token = event.cookies.get('session_token');
if (token) {
request = new Request(request, {
headers: { ...Object.fromEntries(request.headers), Authorization: `Bearer ${token}` }
});
}
}
return fetch(request);
};
TypeScript App.Locals — Typsichere Locals
// src/app.d.ts — Globale TypeScript-Declarations für SvelteKit
import type { User } from '$lib/types';
declare global {
namespace App {
interface Locals {
user: User | null;
session: { id: string; expiresAt: Date } | null;
}
interface Error {
message: string;
errorId?: string;
}
interface PageData {
user?: User | null;
}
interface PageState {
modal?: 'login' | 'register' | null;
}
}
}
export {};
Claude Code Tipp: Beschreibe dein Authentifizierungsschema (JWT, Session, OAuth) und frage Claude nach einer vollständigen Hook-Implementierung. Claude generiert authHook, handleError und app.d.ts als konsistentes System.
6. Adapters & Deployment
SvelteKit ist plattformagnostisch durch sein Adapter-System. Jeder Adapter optimiert den Output für eine spezifische Zielplattform: Node.js-Server, Vercel Edge Functions, Cloudflare Workers oder statische Dateien. Die Wahl des Adapters hat keine Auswirkung auf den Anwendungscode — nur svelte.config.js ändert sich.
DEPLOY
Adapter-Vergleich 2026
| Adapter |
Plattform |
Use Case |
Cold Start |
adapter-node |
Node.js VPS |
Self-hosted, Docker, Railway |
Kein (always-on) |
adapter-vercel |
Vercel Edge/Lambda |
Einfaches Deployment, Auto-Scaling |
~100ms (Edge) |
adapter-cloudflare |
Cloudflare Workers |
Global Edge, KV/D1/R2 |
~0ms |
adapter-static |
Beliebiges CDN |
Reine SPA/Static Sites |
N/A |
adapter-bun |
Bun Runtime |
Schnelle Node-Alternative |
Kein (always-on) |
adapter-node — Self-hosted mit Docker
// svelte.config.js
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
export default {
preprocess: vitePreprocess(),
kit: {
adapter: adapter({
out: 'build',
precompress: true, // Gzip + Brotli
envPrefix: 'PUBLIC_' // PUBLIC_* Variablen im Client
})
}
};
# Dockerfile für SvelteKit adapter-node
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/build ./build
COPY --from=builder /app/package.json .
RUN npm ci --production
EXPOSE 3000
CMD ["node", "build"]
adapter-vercel — Edge Functions
// svelte.config.js
import adapter from '@sveltejs/adapter-vercel';
export default {
kit: {
adapter: adapter({
runtime: 'edge', // oder 'nodejs20.x'
regions: ['fra1', 'iad1'], // Frankfurt + Virginia
isr: {
expiration: 3600 // ISR: 1 Stunde Cache
}
})
}
};
adapter-cloudflare — Workers mit KV und D1
// svelte.config.js
import adapter from '@sveltejs/adapter-cloudflare';
export default {
kit: {
adapter: adapter({
routes: {
include: ['/*'],
exclude: ['/static/*']
}
})
}
};
// In +page.server.ts: Cloudflare Bindings nutzen
export const load: PageServerLoad = async ({ platform }) => {
const { DB, KV, R2 } = platform!.env;
// D1 Database Query
const posts = await DB.prepare('SELECT * FROM posts LIMIT 10').all();
// KV Store lesen
const cached = await KV.get('homepage_data', 'json');
return { posts: posts.results, cached };
};
Environment Variables — PUBLIC_ vs. Private
// .env
DATABASE_URL="postgresql://..." # Nur Server
JWT_SECRET="..." # Nur Server
PUBLIC_APP_URL="https://meine-app.de" # Client + Server
PUBLIC_STRIPE_KEY="pk_live_..." # Client + Server
// Server-only import (sicher)
import { DATABASE_URL, JWT_SECRET } from '$env/static/private';
// Client + Server (PUBLIC_ Prefix pflicht)
import { PUBLIC_APP_URL } from '$env/static/public';
// Dynamische Envs (zur Laufzeit)
import { env } from '$env/dynamic/private';
Claude Code Tipp: Nenne Claude deine Zielplattform (Vercel, Cloudflare, Self-hosted Node) und bestehende Services (Datenbank, Auth-Provider). Claude generiert die passende svelte.config.js, Dockerfile/Deployment-Konfiguration und Environment-Variable-Struktur als vollständiges Setup.
Deployment-Workflow mit Claude Code
# Typischer Prompt für komplettes Deployment-Setup:
"Ich deploye meine SvelteKit App auf einen Ubuntu VPS mit:
- PostgreSQL (lokal via Prisma)
- Nginx als Reverse Proxy
- PM2 für Process Management
- GitHub Actions für CI/CD
Erstelle alle Konfigurationsdateien."
# Claude generiert:
# - svelte.config.js mit adapter-node
# - .github/workflows/deploy.yml
# - nginx.conf mit SSL-Konfiguration
# - ecosystem.config.js für PM2
# - .env.production Template
Zusammenfassung: SvelteKit + Claude Code = Produktivitätsmultiplikator
SvelteKit bietet mit seinem konventionsbasierten Routing, den typsicheren Load Functions und dem progressiven Enhancement-Ansatz von Form Actions eine ideale Grundlage für KI-gestützte Entwicklung. Claude Code versteht die SvelteKit-Konventionen tief genug, um:
- Vollständige Routing-Strukturen mit korrekten Dateikonventionen zu generieren
- Typsichere Load Functions mit Fehlerbehandlung und Caching zu erstellen
- Form Actions mit Zod-Validierung und progressive Enhancement zu implementieren
- Auth-Hooks mit korrekten TypeScript-Locals-Typen zu schreiben
- Adapter-spezifische Deployments für jede Zielplattform zu konfigurieren
Svelte 5 Runes machen den Code dabei lesbarer und expliziter — was Claude Code die Generierung und das Debugging erheblich vereinfacht. Das Ergebnis: Full-Stack TypeScript-Apps in einem Bruchteil der üblichen Entwicklungszeit.
SvelteKit-Modul im Kurs
Im Claude Code Mastery Kurs: vollständiges SvelteKit-Modul mit Svelte 5 Runes, Form Actions, Load Functions, Hooks und Deployment-Adaptern.
14 Tage kostenlos testen →