SvelteKit ist das am schnellsten wachsende Full-Stack-Web-Framework im Jahr 2026. Basierend auf Svelte 5 mit Runes (einem neuen reaktiven primitiven System), macht SvelteKits serverseitiges Rendering, Edge-Deployment und die Zero-JS-by-Default-Philosophie es für viele Projekte zu einer überzeugenden Alternative zu Next.js. Dieser Leitfaden deckt alles von der ersten Komponente bis zur Produktionsbereitstellung ab.
📋 Table of Contents
Was ist neu in Svelte 5 und SvelteKit 2?
- Runen– neue reaktive Grundelemente: $state, $derived, $effect, $props
- Ausschnitte– wiederverwendbare Markup-Blöcke ohne vollständige Komponenten
- Ereignisattribute —
onclickanstatton:click - SvelteKit 2— Flaches Routing, verbesserte Vorspannung, Adapterverbesserungen
- Leistung– Svelte kompiliert das Framework und liefert nur minimales JS
Aufstellen
npm create svelte@latest my-app
cd my-app
npm install
# Choose: SvelteKit, TypeScript, ESLint, Prettier, Playwright, Vitest
Schlanke 5 Komponenten mit Runen
<!-- Counter.svelte — Svelte 5 runes syntax -->
<script lang="ts">
// $state — reactive state (replaces let)
let count = $state(0);
let name = $state('World');
// $derived — computed values (replaces $: derived)
const doubled = $derived(count * 2);
const greeting = $derived(`Hello, ${name}! Count: ${count}`);
// $effect — side effects (replaces $: with side effects)
$effect(() => {
document.title = `Count: ${count}`;
return () => {
document.title = 'App'; // cleanup
};
});
function increment() {
count++;
}
</script>
<div class="counter">
<h2>{greeting}</h2>
<p>Doubled: {doubled}</p>
<!-- Svelte 5: onclick not on:click -->
<button onclick={increment}>+1</button>
<button onclick={() => count = 0}>Reset</button>
<input bind:value={name} placeholder="Your name" />
</div>
<style>
.counter {
padding: 1rem;
border: 1px solid var(--border);
border-radius: 8px;
}
</style>
$props – Komponenten-Requisiten
<!-- Card.svelte -->
<script lang="ts">
interface Props {
title: string;
description?: string;
variant?: 'primary' | 'secondary';
onclick?: () => void;
children?: import('svelte').Snippet;
}
// $props replaces export let
const { title, description = '', variant = 'primary', onclick, children }: Props = $props();
</script>
<article class="card card--{variant}" onclick={onclick}>
<h3>{title}</h3>
{#if description}
<p>{description}</p>
{/if}
{#if children}
{@render children()}
{/if}
</article>
SvelteKit-Routing und Datenladen
src/routes/
+page.svelte — home page
+layout.svelte — shared layout
+error.svelte — error page
blog/
+page.svelte — /blog
+page.server.ts — server-side load
[slug]/
+page.svelte — /blog/my-post
+page.server.ts
api/
users/
+server.ts — REST API routes
// src/routes/blog/+page.server.ts
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ fetch, url }) => {
const page = url.searchParams.get('page') ?? '1';
const response = await fetch(`/api/posts?page=${page}&limit=10`);
if (!response.ok) {
throw error(500, 'Failed to load posts');
}
const { posts, total } = await response.json();
return { posts, total, page: parseInt(page) };
};
<!-- src/routes/blog/+page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
const { data } = $props<{ data: PageData }>();
const { posts, total, page } = $derived(data);
</script>
<svelte:head>
<title>Blog | TechPulse</title>
<meta name="description" content="Latest tech articles" />
</svelte:head>
<main>
<h1>Blog</h1>
{#each posts as post (post.id)}
<article>
<h2><a href="/blog/{post.slug}">{post.title}</a></h2>
<p>{post.excerpt}</p>
<time>{new Date(post.publishedAt).toLocaleDateString()}</time>
</article>
{/each}
<nav>
{#if page > 1}
<a href="?page={page - 1}">Previous</a>
{/if}
{#if posts.length === 10}
<a href="?page={page + 1}">Next</a>
{/if}
</nav>
</main>
API-Routen
// src/routes/api/users/+server.ts
import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async ({ url, locals }) => {
const page = parseInt(url.searchParams.get('page') ?? '1');
const limit = Math.min(parseInt(url.searchParams.get('limit') ?? '20'), 100);
const { users, total } = await locals.db.users.findMany({ page, limit });
return json({ users, total, page, limit });
};
export const POST: RequestHandler = async ({ request, locals }) => {
const body = await request.json();
// Validate with Zod
const result = createUserSchema.safeParse(body);
if (!result.success) {
throw error(422, { message: 'Validation failed', errors: result.error.errors });
}
const user = await locals.db.users.create(result.data);
return json(user, { status: 201 });
};
Formularaktionen
// 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 name = data.get('name') as string;
const email = data.get('email') as string;
const message = data.get('message') as string;
if (!name || !email || !message) {
return fail(422, { name, email, message, error: 'All fields required' });
}
await sendContactEmail({ name, email, message });
throw redirect(303, '/contact?success=true');
}
};
<!-- src/routes/contact/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
const { form } = $props();
</script>
<form method="POST" use:enhance>
{#if form?.error}
<p class="error">{form.error}</p>
{/if}
<input name="name" value={form?.name ?? ''} required placeholder="Name" />
<input name="email" type="email" value={form?.email ?? ''} required placeholder="Email" />
<textarea name="message" required placeholder="Message">{form?.message ?? ''}</textarea>
<button type="submit">Send</button>
</form>
Einsatz
# Vercel (adapter-vercel)
npm install --save-dev @sveltejs/adapter-vercel
# Netlify (adapter-netlify)
npm install --save-dev @sveltejs/adapter-netlify
# Node.js server (adapter-node)
npm install --save-dev @sveltejs/adapter-node
# Cloudflare Workers (adapter-cloudflare)
npm install --save-dev @sveltejs/adapter-cloudflare
# svelte.config.js
import adapter from '@sveltejs/adapter-vercel';
export default { kit: { adapter: adapter() } };
SvelteKit im Jahr 2026 ist ausgereift, schnell und entwicklerfreundlich. Das Runensystem von Svelte 5 ist expliziter als die magische Reaktivität von Svelte 4. Für Projekte, bei denen Bundle-Größe und Laufzeitleistung wichtiger sind als die Größe des Ökosystems, ist SvelteKit eine überzeugende Wahl gegenüber Next.js.
🔗 Share this article
✍️ Leave a Comment