SvelteKit
Svelte 5
TypeScript
Full-Stack
Claude Code
SSR
Form Actions
API Routes
SvelteKit hat sich 2026 als eines der leistungsstärksten Full-Stack-Frameworks für moderne Webanwendungen etabliert. Mit Svelte 5 und dem neuen Runes-System, typsicherem TypeScript und einem ausgefeilten Routing-Modell ist SvelteKit die erste Wahl für Entwickler, die schnelle, wartbare Applikationen bauen wollen — ohne den Overhead komplexer Frameworks.
Claude Code versteht SvelteKit in all seinen Facetten: File-based Routing, Load Functions, Form Actions mit progressiver Verbesserung, API-Routes, Server-Side Rendering und den Einsatz verschiedener Adapter für Vercel, Cloudflare oder Node.js. In diesem Guide zeigen wir, wie Claude Code dir bei jedem dieser Konzepte als kompetenter Entwicklungspartner zur Seite steht.
1. SvelteKit Grundlagen: Routing & Svelte 5 Runes
SvelteKit nutzt ein dateibasiertes Routing-System. Jede Route entspricht einem Verzeichnis in src/routes/. Spezielle Dateinamen wie +page.svelte, +layout.svelte und +error.svelte steuern das Rendering. Claude Code kennt diese Konventionen auswendig und generiert sofort korrekte Dateistrukturen.
📄
+page.svelte
Seitenkomponente, die für die jeweilige Route gerendert wird.
🗂
+layout.svelte
Wiederverwendbares Layout für alle Kind-Routen mit <slot />.
⚠️
+error.svelte
Fehlerseite für HTTP-Fehler mit Zugriff auf $page.error.
🔄
+page.ts / +page.server.ts
Load Functions für Datenabruf — universal oder server-exklusiv.
Svelte 5 Runes: Reaktivität neu gedacht
Svelte 5 hat das Reaktivitäts-Modell grundlegend überarbeitet. Statt impliziter Reaktivität durch let-Variablen gibt es jetzt explizite Runes: $state, $derived, $effect und $props. Claude Code generiert modernen Svelte-5-Code mit diesen Runes von Anfang an.
src/routes/+page.svelte — Svelte 5 Runes
<script lang="ts">
// $state: reaktive Variable (ersetzt `let`)
let count = $state(0);
let name = $state('');
// $derived: berechneter Wert (ersetzt $:)
let doubled = $derived(count * 2);
let greeting = $derived(
name ? `Hallo, ${name}!` : 'Gib deinen Namen ein.'
);
// $effect: Seiteneffekte (ersetzt reactive statements)
$effect(() => {
console.log(`Count geändert: ${count}`);
// Cleanup-Funktion optional zurückgeben
return () => console.log('Cleanup');
});
function increment() {
count++;
}
</script>
<div class="counter">
<h1>{greeting}</h1>
<input bind:value={name} placeholder="Name eingeben" />
<button onclick={increment}>
Klicks: {count} (doppelt: {doubled})
</button>
</div>
Layout & Nested Routes
src/routes/+layout.svelte — Root Layout
<script lang="ts">
import { page } from '$app/stores';
import Nav from '$lib/components/Nav.svelte';
// Props für Layout: data kommt von +layout.ts
let { data, children } = $props();
</script>
<Nav user={data.user} />
<main class="app-container">
<!-- Kind-Routen werden hier gerendert -->
{@render children()}
</main>
<footer>
<p>Aktuelle Route: {$page.url.pathname}</p>
</footer>
💡 Claude Code Tipp
Mit dem Befehl "Erstelle eine SvelteKit-Route /dashboard mit Layout, Error-Seite und Load Function" generiert Claude Code alle vier Dateien (+page.svelte, +layout.svelte, +error.svelte, +page.server.ts) mit korrekten TypeScript-Typen in einem Schritt.
$state
$derived
$effect
$props
Svelte 5 Runes auf einen Blick
$state(value) — reaktive Variable, ersetzt let
$derived(expr) — berechnet aus State, ersetzt $: variable = ...
$effect(() => {}) — Seiteneffekte, ersetzt $: { ... }
$props() — Komponentenprops typsicher destrukturieren
$bindable() — zwei-Wege-Bindung für Props erlauben
2. Load Functions: Daten laden mit TypeScript
SvelteKit trennt strikt zwischen Datenabruf und Rendering. Load Functions in +page.ts (universal) oder +page.server.ts (server-only) versorgen die Seite mit Daten, bevor sie gerendert wird. Claude Code versteht den Unterschied zwischen beiden und wählt den richtigen Typ automatisch.
Universal Load Function (+page.ts)
Läuft sowohl auf dem Server (beim ersten Request) als auch im Browser (bei Navigation). Kein Zugriff auf cookies oder locals.
src/routes/produkte/+page.ts
import type { PageLoad } from './$types';
// SvelteKit inferiert den Rückgabetyp automatisch
export const load: PageLoad = async ({ fetch, params, url, depends }) => {
// depends() markiert Abhängigkeiten für invalidate()
depends('app:produkte');
const kategorie = url.searchParams.get('kategorie') ?? 'alle';
// fetch ist SvelteKit-aware: relativ-URLs funktionieren
const response = await fetch(`/api/produkte?kategorie=${kategorie}`);
if (!response.ok) {
// SvelteKit Error-Handling
throw new Error(`Fehler beim Laden: ${response.status}`);
}
const produkte = await response.json();
return {
produkte,
kategorie,
geladenAm: new Date().toISOString()
};
};
Server Load Function (+page.server.ts)
Läuft ausschließlich auf dem Server. Hat Zugriff auf Cookies, Datenbankverbindungen und event.locals (z.B. Session-Daten aus Hooks).
src/routes/dashboard/+page.server.ts
import type { PageServerLoad } from './$types';
import { redirect, error } from '@sveltejs/kit';
import { db } from '$lib/server/db';
export const load: PageServerLoad = async ({ cookies, locals, depends }) => {
// Auth-Check über event.locals (gesetzt in hooks.server.ts)
if (!locals.user) {
redirect(307, '/login');
}
depends('app:dashboard');
// Direkter DB-Zugriff — nur serverseitig!
const [bestellungen, statistiken] = await Promise.all([
db.query(`
SELECT b.id, b.erstellt_am, b.gesamtbetrag, b.status
FROM bestellungen b
WHERE b.nutzer_id = $1
ORDER BY b.erstellt_am DESC
LIMIT 10
`, [locals.user.id]),
db.query(`
SELECT COUNT(*) as gesamt, SUM(gesamtbetrag) as umsatz
FROM bestellungen WHERE nutzer_id = $1
`, [locals.user.id])
]);
return {
user: locals.user,
bestellungen: bestellungen.rows,
statistiken: statistiken.rows[0]
};
};
invalidate() und depends()
src/routes/dashboard/+page.svelte — Daten neu laden
<script lang="ts">
import { invalidate } from '$app/navigation';
let { data } = $props();
async function refresh() {
// Löst alle Load Functions mit depends('app:dashboard') neu aus
await invalidate('app:dashboard');
}
</script>
<h1>Willkommen, {data.user.name}</h1>
<p>Gesamtumsatz: {data.statistiken.umsatz} €</p>
<button onclick={refresh}>Daten aktualisieren</button>
{#each data.bestellungen as bestellung}
<div class="bestellung">
<span>{bestellung.id}</span>
<span>{bestellung.gesamtbetrag} €</span>
<span>{bestellung.status}</span>
</div>
{/each}
ℹ️ Universal vs. Server Load
Nutze +page.ts wenn Daten im Browser gecached werden können (öffentliche APIs, keine Auth). Nutze +page.server.ts für Datenbankzugriffe, Auth-Checks und sensible Logik die nie den Browser erreichen soll.
Form Actions sind eines der mächtigsten Features von SvelteKit. Sie ermöglichen typsichere Server-seitige Formularverarbeitung mit optionaler progressiver Verbesserung — Formulare funktionieren auch ohne JavaScript. Claude Code generiert vollständige Action-Handler mit Validierung, Fehlerbehandlung und use:enhance.
src/routes/kontakt/+page.server.ts — Form Actions mit Zod
import type { Actions, PageServerLoad } from './$types';
import { fail, redirect } from '@sveltejs/kit';
import { z } from 'zod';
import { sendMail } from '$lib/server/mailer';
// Zod-Schema für Formularvalidierung
const kontaktSchema = z.object({
name: z.string().min(2, 'Name zu kurz').max(100),
email: z.string().email('Ungültige E-Mail-Adresse'),
nachricht: z.string().min(10, 'Nachricht zu kurz').max(2000),
betreff: z.enum(['allgemein', 'support', 'sales'])
});
export const actions: Actions = {
// Default Action: aufgerufen ohne ?/action=name
default: async ({ request, getClientAddress }) => {
const formData = await request.formData();
// Alle Felder aus FormData extrahieren
const rawData = {
name: formData.get('name'),
email: formData.get('email'),
nachricht: formData.get('nachricht'),
betreff: formData.get('betreff')
};
// Zod-Validierung
const result = kontaktSchema.safeParse(rawData);
if (!result.success) {
// fail() setzt HTTP 422 und gibt Fehler zurück
return fail(422, {
fehler: result.error.flatten().fieldErrors,
werte: rawData // Eingaben zurückgeben für Re-Fill
});
}
try {
await sendMail({
an: 'kontakt@example.com',
von: result.data.email,
betreff: `[${result.data.betreff}] ${result.data.name}`,
text: result.data.nachricht
});
} catch (err) {
return fail(500, { fehler: { global: 'E-Mail konnte nicht gesendet werden.' } });
}
// Erfolgreiche Umleitung
redirect(303, '/kontakt/danke');
},
// Named Action: ?/newsletter
newsletter: async ({ request }) => {
const data = await request.formData();
const email = data.get('email') as string;
// Newsletter-Logik ...
return { erfolg: true, email };
}
};
src/routes/kontakt/+page.svelte — use:enhance
<script lang="ts">
import { enhance } from '$app/forms';
import type { ActionData } from './$types';
let { form }: { form: ActionData } = $props();
let loading = $state(false);
</script>
<!-- use:enhance aktiviert progressives JS ohne page-reload -->
<form
method="POST"
use:enhance={({ formElement, formData, action, cancel, submitter }) => {
loading = true;
// Vor-Submit-Hook: cancel() zum Abbrechen
return async ({ result, update }) => {
loading = false;
// update() übernimmt das Standard-SvelteKit-Verhalten
await update();
};
}}
>
<div class="field">
<label for="name">Name</label>
<input
id="name"
name="name"
value={form?.werte?.name ?? ''}
class:fehler={form?.fehler?.name}
/>
{#if form?.fehler?.name}
<span class="fehler-text">{form.fehler.name[0]}</span>
{/if}
</div>
<button type="submit" disabled={loading}>
{loading ? 'Wird gesendet...' : 'Nachricht senden'}
</button>
</form>
Action
fail()
redirect()
Form Actions Cheatsheet
fail(status, data) — Fehler mit HTTP-Statuscode zurückgeben
redirect(status, url) — Nach Erfolg umleiten (303 für POST-Redirect-GET)
use:enhance — JavaScript-Enhancement ohne Seitenneuladen
- Named Actions:
?/aktionsname im form-action-Attribut
- Progressive Enhancement: Funktioniert auch ohne JavaScript!
4. API Routes: REST-Endpunkte mit +server.ts
SvelteKit kann nicht nur Seiten rendern — mit +server.ts-Dateien werden vollwertige REST-API-Endpunkte erstellt. Diese laufen immer auf dem Server und können auf Datenbanken, externe APIs und alle Node.js-Module zugreifen. Claude Code schreibt typsichere Handler für alle HTTP-Methoden.
src/routes/api/produkte/+server.ts — REST API
import type { RequestHandler } from './$types';
import { json, error } from '@sveltejs/kit';
import { db } from '$lib/server/db';
import { z } from 'zod';
const produktSchema = z.object({
name: z.string().min(1),
preis: z.number().positive(),
kategorie: z.string(),
beschreibung: z.string().optional()
});
// GET /api/produkte
export const GET: RequestHandler = async ({ url, locals }) => {
const kategorie = url.searchParams.get('kategorie');
const limit = parseInt(url.searchParams.get('limit') ?? '20');
const query = kategorie
? `SELECT * FROM produkte WHERE kategorie = $1 LIMIT $2`
: `SELECT * FROM produkte LIMIT $1`;
const params = kategorie ? [kategorie, limit] : [limit];
const { rows } = await db.query(query, params);
// json() setzt Content-Type: application/json automatisch
return json(rows, {
headers: {
'Cache-Control': 'public, max-age=60'
}
});
};
// POST /api/produkte
export const POST: RequestHandler = async ({ request, locals }) => {
// Auth-Check
if (!locals.user?.istAdmin) {
error(403, 'Nur Admins dürfen Produkte erstellen');
}
const body = await request.json();
const validiert = produktSchema.safeParse(body);
if (!validiert.success) {
error(400, { message: 'Validierungsfehler', fehler: validiert.error.flatten() });
}
const { rows } = await db.query(
`INSERT INTO produkte (name, preis, kategorie, beschreibung)
VALUES ($1, $2, $3, $4) RETURNING *`,
[validiert.data.name, validiert.data.preis, validiert.data.kategorie, validiert.data.beschreibung]
);
return json(rows[0], { status: 201 });
};
// DELETE /api/produkte
export const DELETE: RequestHandler = async ({ url, locals }) => {
if (!locals.user?.istAdmin) error(403, 'Keine Berechtigung');
const id = url.searchParams.get('id');
if (!id) error(400, 'ID erforderlich');
await db.query('DELETE FROM produkte WHERE id = $1', [id]);
return json({ geloescht: true });
};
CORS-Setup für externe API-Clients
src/routes/api/public/+server.ts — CORS
import type { RequestHandler } from './$types';
import { json } from '@sveltejs/kit';
const CORS_HEADERS = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
};
// OPTIONS-Handler für CORS Preflight
export const OPTIONS: RequestHandler = () => {
return new Response(null, { status: 204, headers: CORS_HEADERS });
};
export const GET: RequestHandler = async () => {
return json(
{ version: '1.0', status: 'ok' },
{ headers: CORS_HEADERS }
);
};
⚠️ Sicherheitshinweis
Access-Control-Allow-Origin: * erlaubt Zugriff von jeder Domain. Für APIs mit Authentifizierung immer spezifische Domains angeben und nie * mit credentials: include kombinieren.
5. SSR vs. SSG vs. CSR: Rendering-Strategien
SvelteKit ist außergewöhnlich flexibel bei Rendering-Strategien. Du kannst pro Route entscheiden: Server-Side Rendering (SSR), Static Site Generation (SSG) oder Client-Side Rendering (CSR). Claude Code hilft dir, die richtige Strategie für jeden Use Case zu wählen.
| Strategie |
Wann gerendert |
SEO |
Dynamische Daten |
Server nötig |
| SSR |
Jeder Request |
✓ Optimal |
✓ Ja |
✓ Ja |
| SSG |
Build-Zeit |
✓ Optimal |
✗ Nein |
✗ Nein |
| CSR |
Im Browser |
~ Eingeschränkt |
✓ Ja |
✗ Nein |
| Edge SSR |
Request (Edge) |
✓ Optimal |
✓ Ja |
~ Edge Runtime |
src/routes/blog/[slug]/+page.ts — SSG Konfiguration
import type { EntryGenerator, PageLoad } from './$types';
// Alle statischen Seiten vorher generieren
export const entries: EntryGenerator = async () => {
const response = await fetch('https://api.example.com/blog/slugs');
const slugs: string[] = await response.json();
return slugs.map(slug => ({ slug }));
};
// prerender: true → Static Site Generation
export const prerender = true;
// ssr: false → nur Client-Side Rendering
// export const ssr = false;
// csr: false → kein JavaScript im Browser (Pure HTML)
// export const csr = false;
export const load: PageLoad = async ({ params, fetch }) => {
const response = await fetch(`/api/blog/${params.slug}`);
const artikel = await response.json();
return { artikel };
};
Adapter: SvelteKit auf verschiedene Plattformen deployen
svelte.config.ts — Adapter-Konfiguration
import adapter from '@sveltejs/adapter-vercel';
// Alternativen:
// import adapter from '@sveltejs/adapter-cloudflare';
// import adapter from '@sveltejs/adapter-node';
// import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter({
// Vercel Edge Runtime für ultra-niedrige Latenz
runtime: 'edge',
// Regionen für Edge-Deployment
regions: ['fra1', 'iad1']
}),
alias: {
// $lib ist standardmäßig auf src/lib gemappt
'$components': 'src/lib/components',
'$server': 'src/lib/server'
}
}
};
export default config;
SSR
SSG
Edge
Adapter-Übersicht
adapter-vercel — Vercel mit Edge Runtime Support
adapter-cloudflare — Cloudflare Workers/Pages
adapter-node — Node.js Server (selbst gehosted)
adapter-static — Rein statisch (alle Seiten als HTML)
adapter-netlify — Netlify Functions
6. Auth & Deployment: Hooks, Sessions, SvelteKit Auth
Authentication in SvelteKit erfolgt über Hooks. Die Datei src/hooks.server.ts ist der Einstiegspunkt für jeden Request — ideal um Sessions zu validieren, Benutzer zu laden und in event.locals zu speichern. Claude Code generiert vollständige Auth-Pipelines auf Basis dieses Patterns.
src/hooks.server.ts — Session-Management
import type { Handle, HandleServerError } from '@sveltejs/kit';
import { sequence } from '@sveltejs/kit/hooks';
import { db } from '$lib/server/db';
import { verifyJWT } from '$lib/server/auth';
// Auth-Hook: läuft bei JEDEM Request
const authHook: Handle = async ({ event, resolve }) => {
const sessionToken = event.cookies.get('session');
if (sessionToken) {
try {
// JWT verifizieren und User-Daten laden
const payload = await verifyJWT(sessionToken);
const user = await db.findUser(payload.userId);
if (user) {
// In locals speichern → verfügbar in allen Load Functions + Actions
event.locals.user = {
id: user.id,
name: user.name,
email: user.email,
istAdmin: user.rolle === 'admin'
};
}
} catch {
// Ungültiger Token → Cookie löschen
event.cookies.delete('session', { path: '/' });
}
}
return resolve(event);
};
// Security-Hook: Security Headers setzen
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(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' 'unsafe-inline';"
);
return response;
};
// sequence() kombiniert mehrere Hooks
export const handle = sequence(authHook, securityHook);
// Globaler Error-Handler
export const handleError: HandleServerError = async ({ error, event }) => {
console.error('Server-Fehler:', error, event.url.toString());
return {
message: 'Interner Server-Fehler',
code: 'INTERNAL_ERROR'
};
};
TypeScript: app.d.ts Typen
src/app.d.ts — Globale Typen
// Typen für SvelteKit Plattform-APIs erweitern
declare global {
namespace App {
// event.locals Typen
interface Locals {
user: {
id: string;
name: string;
email: string;
istAdmin: boolean;
} | null;
}
// Globale Fehler-Struktur
interface Error {
message: string;
code?: string;
}
// Platform-spezifische Typen (Cloudflare, Vercel etc.)
interface Platform {
env?: {
DATABASE_URL: string;
JWT_SECRET: string;
};
}
}
}
export {};
Deployment: Vercel in 3 Schritten
1
Adapter installieren
npm install @sveltejs/adapter-vercel — dann in svelte.config.ts einbinden.
2
Umgebungsvariablen setzen
In Vercel Dashboard: DATABASE_URL, JWT_SECRET und weitere Secrets konfigurieren.
3
Deploy via Git
git push origin main — Vercel erkennt SvelteKit automatisch und deployt auf Edge-Netzwerk.
src/routes/login/+page.server.ts — Login Action
import type { Actions } from './$types';
import { fail, redirect } from '@sveltejs/kit';
import { db } from '$lib/server/db';
import { createJWT, verifyPassword } from '$lib/server/auth';
export const actions: Actions = {
default: async ({ request, cookies }) => {
const data = await request.formData();
const email = data.get('email') as string;
const passwort = data.get('passwort') as string;
if (!email || !passwort) {
return fail(400, { fehler: 'E-Mail und Passwort erforderlich' });
}
const user = await db.findUserByEmail(email);
if (!user || !await verifyPassword(passwort, user.passwortHash)) {
// Vage Fehlermeldung verhindert User-Enumeration
return fail(401, { fehler: 'E-Mail oder Passwort falsch' });
}
const token = await createJWT({ userId: user.id });
cookies.set('session', token, {
httpOnly: true, // Kein JS-Zugriff
secure: true, // Nur HTTPS
sameSite: 'lax', // CSRF-Schutz
path: '/',
maxAge: 60 * 60 * 24 * 7 // 7 Tage
});
redirect(303, '/dashboard');
}
};
✅ Claude Code in Action
Beschreibe dein Datenbankschema und Claude Code generiert sofort passende Load Functions, Form Actions und API Routes — inklusive Typen, Validierung und Fehlerbehandlung. Kein Boilerplate, sofort produktionsreif.
Fazit: SvelteKit + Claude Code = Full-Stack Power 2026
SvelteKit bietet 2026 ein ausgereiftes, performantes Full-Stack-Framework mit einem klaren mentalen Modell: Dateibasiertes Routing, typsichere Load Functions, progressive Form Actions und flexible Rendering-Strategien. Das Runes-System von Svelte 5 macht Reaktivität explizit und besser testbar.
Claude Code kennt das gesamte SvelteKit-Ökosystem und generiert produktionsreife Implementierungen — von der einfachen Komponente bis zum vollständigen Auth-System mit Hooks, Sessions und CSRF-Schutz. Die Kombination aus SvelteKit und Claude Code ist einer der schnellsten Wege zu modernen, wartbaren Full-Stack-Applikationen.
- ✅ File-based Routing — Intuitive Projektstruktur ohne Konfiguration
- ✅ Svelte 5 Runes — Explizite, typsichere Reaktivität
- ✅ Form Actions — Server-seitige Formulare mit progressiver Verbesserung
- ✅ TypeScript everywhere — Automatisch generierte Typen aus Routing
- ✅ Flexible Adapter — Vercel, Cloudflare, Node.js, Static
- ✅ Claude Code — Generiert SvelteKit-Code auf Produktionsniveau
SvelteKit-Apps mit KI entwickeln
Starte kostenlos und lass Claude Code deine nächste Full-Stack-App mit SvelteKit, TypeScript und modernen Best Practices bauen.
Jetzt kostenlos testen →