SvelteKit & Full-Stack 2026

SvelteKit mit Claude Code:
Full-Stack Web Apps 2026

Routing, Load Functions, Form Actions, SSR/SSG, Adapter, Svelte 5 Runes — moderne Full-Stack-Apps mit TypeScript und Claude Code.

✍️ 6. Mai 2026 ⏱ 11 min Lesezeit 🛠 SvelteKit · Svelte 5 · TypeScript
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.

📋 Inhaltsverzeichnis

  1. SvelteKit Grundlagen: Routing & Svelte 5 Runes
  2. Load Functions: Daten laden mit TypeScript
  3. Form Actions: Server-seitige Formulare
  4. API Routes: REST-Endpunkte mit +server.ts
  5. SSR vs. SSG vs. CSR: Rendering-Strategien
  6. Auth & Deployment: Hooks, Sessions, Adapter

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

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.

3. Form Actions: Server-seitige Formulare

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

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

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.

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 →