Svelte- und SvelteKit-Entwicklung in Cursor: Ein vollständiger Leitfaden

Svelte ist ein compilerbasiertes UI-Framework, das minimales JavaScript an den Browser sendet, und SvelteKit ist sein Full-Stack-Anwendungsframework. Svelte 5 führte Runes ein, ein neues Reaktivitätsmodell, das expliziter und leistungsstärker ist als die bisherige let-basierte Reaktivität. Cursor ist mit seiner VS Code-kompatiblen Basis und dem integrierten KI-Assistenten ein hervorragender Editor für Svelte-Arbeit — er versteht .svelte-Dateien, bietet umfangreiches IntelliSense und kann Komponenten aus natürlichsprachlichen Prompts generieren.
Dieser Leitfaden behandelt den vollständigen Svelte-Entwicklungsworkflow in Cursor: Einrichtung der Toolchain, Beherrschung von Svelte 5 Runes, Erstellung von SvelteKit-Apps mit dateibasiertem Routing, Verwaltung gemeinsamer Zustände mit Stores und Nutzung der KI zum Generieren und Refactorn von Komponenten.
Einrichtung der Svelte-Entwicklungsumgebung in Cursor
Voraussetzungen
Svelte 5 und SvelteKit benötigen Node.js 18 oder neuer. Überprüfen Sie Ihre Installation:
node --version
npm --version
Wenn Sie eine neuere Version benötigen, verwenden Sie fnm oder nvm:
fnm install 20
fnm use 20
Das integrierte Terminal von Cursor (Ctrl+`) merkt sich Ihr aktuelles Arbeitsverzeichnis, sodass Sie ein Projekt scaffolden und sofort Dateien öffnen können, ohne den Kontext zu wechseln.
Installation der Svelte-Erweiterung
Cursor verlässt sich für die Sprachunterstützung auf die offizielle Svelte-Erweiterung. Installieren Sie sie aus der Erweiterungsansicht (Ctrl+Shift+X):
- Suchen Sie nach "Svelte".
- Installieren Sie die vom Svelte-Team veröffentlichte Erweiterung.
- Laden Sie Cursor neu, falls aufgefordert.
Die Erweiterung bietet:
- Syntaxhervorhebung für
.svelte-Dateien. - Diagnose und Typprüfung über
svelte-language-server. - Formatierung durch Prettier mit dem
prettier-plugin-svelte-Paket. - IntelliSense für Komponenten-Imports und Props.
Wenn Sie zuvor eine Community-Svelte-Erweiterung verwendet haben, deaktivieren Sie diese vor der Installation der offiziellen. Konflikterweiterungen verursachen doppelte Diagnosen und unzuverlässige Formatierung.
Scaffolding eines SvelteKit-Projekts
Verwenden Sie die offizielle sv-CLI, um eine neue SvelteKit-Anwendung zu erstellen:
npx sv create my-svelte-app
Der interaktive Prompt bietet Voreinstellungen:
? Which template would you like? SvelteKit minimal
? Add type checking with TypeScript? Yes, using TypeScript syntax
? Add ESLint for code linting? Yes
? Add Prettier for code formatting? Yes
? Add Playwright for browser testing? No
? Add Vitest for unit testing? Yes
Öffnen Sie das Projekt in Cursor:
cd my-svelte-app
cursor .
npm install
npm run dev
Der Entwicklungsserver startet unter http://localhost:5173.
Sie können später Funktionen hinzufügen, ohne das Projekt neu zu erstellen. Der Befehl sv add installiert Integrationen wie Tailwind CSS, Storybook oder Drizzle auf einer bestehenden App:
npx sv add tailwindcss
npx sv add storybook
Svelte 5 Runes meistern
Was sind Runes?
Runes sind spezielle compilererkannte Funktionen, die in Svelte 5 eingeführt wurden. Sie ersetzen die implizite Reaktivität von Svelte 4 durch ein explizites, vorhersagbares Modell. Die Kern-Runes sind:
$state— deklariert einen reaktiven Zustand.$derived— deklariert einen aus anderem reaktiven Zustand berechneten Wert.$effect— führt Seiteneffekte aus, wenn sich Abhängigkeiten ändern.$props— deklariert Komponenten-Props.$bindable— markiert eine Prop als bidirektional bindbar.
Zustand mit $state deklarieren
<script lang="ts">
let count = $state(0);
let user = $state({ name: 'Ada', age: 36 });
function increment() {
count++;
}
</script>
<button onclick={increment}>
Count: {count}
</button>
<p>{user.name} is {user.age}</p>
In Svelte 5 verwenden Event-Handler das Attribut onclick (Kleinbuchstaben, kein on:-Präfix) anstelle des alten on:click. Die neue Syntax ist näher an Standard-DOM-Attributen und funktioniert besser mit TypeScript.
Abgeleitete Werte mit $derived
<script lang="ts">
let price = $state(100);
let quantity = $state(2);
let total = $derived(price * quantity);
let formatted = $derived(
new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(total)
);
</script>
<p>Price: $ {price}</p>
<p>Quantity: {quantity}</p>
<p>Total: {formatted}</p>
Seiteneffekte mit $effect
<script lang="ts">
let theme = $state<'light' | 'dark'>('light');
$effect(() => {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
});
</script>
<button onclick={() => (theme = theme === 'light' ? 'dark' : 'light')}>
Toggle theme
</button>
$effect wird ausgeführt, nachdem das DOM aktualisiert wurde. Verwenden Sie es nicht, um Werte zu berechnen, die das Rendering beeinflussen — verwenden Sie stattdessen $derived. Effects dienen der Synchronisation mit externen Systemen (APIs, localStorage, Drittanbieter-Bibliotheken).
Komponenten-Props mit $props
<script lang="ts">
interface Props {
title: string;
count?: number;
onIncrement?: (value: number) => void;
}
let { title, count = 0, onIncrement }: Props = $props();
function handleClick() {
count++;
onIncrement?.(count);
}
</script>
<h2>{title}</h2>
<button onclick={handleClick}>Clicked {count} times</button>
Bidirektionale Bindung mit $bindable
<!-- TextInput.svelte -->
<script lang="ts">
interface Props {
value?: string;
}
let { value = $bindable('') }: Props = $props();
</script>
<input bind:value />
Verwendung in einem Parent:
<script lang="ts">
import TextInput from './TextInput.svelte';
let name = $state('');
</script>
<TextInput bind:value={name} />
<p>Hello, {name}!</p>
$bindable ist Svelte 5s Ersatz für das export let + bind:value-Pattern. Es macht den bidirektionalen Bindungsvertrag in der Typsignatur der Kind-Komponente explizit.
SvelteKit-Anwendungen erstellen
Dateibasiertes Routing
SvelteKit verwendet dateibasiertes Routing im Verzeichnis src/routes. Jede +page.svelte-Datei wird zu einer Seite, und +layout.svelte-Dateien umschließen verschachtelte Seiten.
src/routes/
├── +layout.svelte
├── +page.svelte → /
├── about/
│ └── +page.svelte → /about
├── blog/
│ ├── +page.svelte → /blog
│ └── [slug]/
│ └── +page.svelte → /blog/:slug
└── +layout.svelte
Daten mit +page.server.ts laden
// src/routes/blog/[slug]/+page.server.ts
import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
interface Post {
slug: string;
title: string;
body: string;
}
export const load: PageServerLoad = async ({ params }) => {
const response = await fetch(`https://api.example.com/posts/${params.slug}`);
if (!response.ok) {
throw error(404, 'Post not found');
}
const post: Post = await response.json();
return { post };
};
Daten in der Seite konsumieren:
<!-- src/routes/blog/[slug]/+page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
let { data }: { data: PageData } = $props();
</script>
<article>
<h1>{data.post.title}</h1>
<p>{data.post.body}</p>
</article>
Das $types-Modul wird von SvelteKit automatisch generiert. Es gibt Ihnen vollständig typisierte load-Funktionen und data-Props. Cursors IntelliSense liest diese Typen automatisch, sodass Sie Autovervollständigung für data.post.title erhalten.
Form-Aktionen
SvelteKit verarbeitet Formularübermittlungen mit Aktionen, die in +page.server.ts definiert sind:
// src/routes/login/+page.server.ts
import { fail, redirect } from '@sveltejs/kit';
import type { Actions } from './$types';
export const actions: Actions = {
default: async ({ request, cookies }) => {
const formData = await request.formData();
const email = formData.get('email');
const password = formData.get('password');
if (!email || !password) {
return fail(400, { message: 'Missing credentials' });
}
const token = await authenticate(String(email), String(password));
if (!token) {
return fail(401, { message: 'Invalid credentials' });
}
cookies.set('session', token, { path: '/' });
throw redirect(303, '/dashboard');
},
};
Die entsprechende Seite verwendet die enhance-Aktion für progressive enhancement:
<!-- src/routes/login/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
import type { PageData } from './$types';
let { form }: { form: PageData } = $props();
</script>
<form method="POST" use:enhance>
<label>Email <input name="email" type="email" /></label>
<label>Password <input name="password" type="password" /></label>
{#if form?.message}
<p class="error">{form.message}</p>
{/if}
<button type="submit">Log in</button>
</form>
Serverseitige +page.server.ts-Dateien werden nur auf dem Server ausgeführt. Importieren Sie niemals Browser-only-APIs (wie localStorage) in diese, und platzieren Sie niemals Secrets in +page.ts (dem universellen load), das ebenfalls im Browser ausgeführt werden kann.
Layouts und Layout-Gruppen
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import type { Snippet } from 'svelte';
import Nav from '../components/Nav.svelte';
let { children }: { children: Snippet } = $props();
</script>
<div class="app">
<Nav />
<main>{@render children()}</main>
</div>
In Svelte 5 ersetzt das children-Snippet die veraltete <slot />-Syntax.
Verwaltung gemeinsamer Zustände mit Stores
Svelte Stores Grundlagen
Auch mit Runes bleiben Svelte Stores für komponentenübergreifende Zustände nützlich. Ein Store ist jedes Objekt mit einer subscribe-Methode.
// lib/stores/user.ts
import { writable } from 'svelte/store';
interface User {
id: string;
name: string;
}
export const currentUser = writable<User | null>(null);
Store mit dem $-Präfix konsumieren:
<script lang="ts">
import { currentUser } from '$lib/stores/user';
</script>
{#if $currentUser}
<p>Welcome, {$currentUser.name}</p>
{:else}
<p>Please log in.</p>
{/if}
Readable- und Derived-Stores
import { readable, derived } from 'svelte/store';
export const time = readable(new Date(), (set) => {
const interval = setInterval(() => set(new Date()), 1000);
return () => clearInterval(interval);
});
export const formattedTime = derived(time, ($time) =>
$time.toLocaleTimeString('en-US')
);
Runes und Stores kombinieren
Sie können Stores innerhalb von $effect verwenden, um Legacy-Code mit Runes zu überbrücken:
<script lang="ts">
import { currentUser } from '$lib/stores/user';
import { get } from 'svelte/store';
let userSnapshot = $state(get(currentUser));
$effect(() => {
const unsubscribe = currentUser.subscribe((value) => {
userSnapshot = value;
});
return unsubscribe;
});
</script>
Für neue Projekte wird ein Runes-basiertes gemeinsamer-Zustand-Pattern mit einer Klasse mit $state-Feldern und der Svelte-Context-API empfohlen. Dies vermeidet den Store-Boilerplate bei gleichzeitiger Beibehaltung der Reaktivität:
// lib/state/counter.svelte.ts
export class CounterState {
count = $state(0);
increment() {
this.count++;
}
}
KI-gestützte Svelte-Komponentengenerierung
Komponenten aus Prompts generieren
Cursors KI ist hervorragend darin, Svelte 5-Komponenten mit Runes zu erstellen. Öffnen Sie einen Chat (Ctrl+Shift+P → "Cursor: New Chat") oder verwenden Sie Inline-Bearbeitungen (Ctrl+K) und beschreiben Sie, was Sie benötigen:
Create a Svelte 5 component (script lang="ts") for an accessible accordion.
It should accept an items prop: array of { id, title, content }. Use runes
for state (which panel is open). Include keyboard navigation (arrow keys)
and scoped styles.
Cursor generiert eine vollständige .svelte-Datei. Überprüfen Sie die Ausgabe und iterieren Sie — bitten Sie es, eine allowMultiple-Prop oder Animationsübergänge hinzuzufügen.
Migration von Svelte 4 zu Svelte 5
Wenn Sie eine ältere Codebasis pflegen, kann Cursor Komponenten zur Runes-Syntax migrieren. Markieren Sie die Datei, drücken Sie Ctrl+K, und geben Sie ein:
Migrate this Svelte 4 component to Svelte 5 runes syntax. Replace `let`
reactivity with $state, `$:` with $derived/$effect, `export let` with $props,
and `createEventDispatcher` with callback props. Preserve all behavior.
Runes-Migration ändert das Reaktivitäts-Timing auf subtile Weise. Führen Sie nach dem Refactoring Ihre Testsuite aus und überprüfen Sie manuell jede Komponente, die Lifecycle-Hooks oder Watcher verwendet, da $effect unter anderen Bedingungen ausgelöst wird als das alte afterUpdate.
End-to-End-Tests generieren
Kombinieren Sie Playwright mit KI-generierten Tests:
// tests/accordion.spec.ts
import { test, expect } from '@playwright/test';
test('accordion expands on click', async ({ page }) => {
await page.goto('/');
await page.getByRole('button', { name: 'Section 1' }).click();
await expect(page.getByText('Content of section 1')).toBeVisible();
});
Fragen Sie Cursor: "Generate Playwright tests for Accordion.svelte covering expand/collapse and keyboard navigation."
Cursor für Svelte-Projekte konfigurieren
Workspace-Einstellungen
Erstellen Sie .vscode/settings.json, um die Formatierung im gesamten Team zu standardisieren:
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "svelte.svelte-vscode",
"svelte.plugin.svelte.format.config.singleQuote": true,
"svelte.plugin.svelte.format.config.semi": true,
"svelte.plugin.svelte.note-new-transformation": false,
"typescript.tsdk": "node_modules/typescript/lib"
}
Prettier mit Svelte-Plugin
npm install -D prettier prettier-plugin-svelte
.prettierrc:
{
"singleQuote": true,
"semi": true,
"plugins": ["prettier-plugin-svelte"],
"overrides": [
{
"files": "*.svelte",
"options": { "parser": "svelte" }
}
]
}
Prettier formatiert die <script>- und <style>-Blöcke, lässt aber das Svelte-Template-Markup absichtlich unangetastet, um Direktiven nicht zu beschädigen. Verlassen Sie sich für die Template-Formatierung auf den integrierten Formatierer der Svelte-Erweiterung.
ESLint-Konfiguration
// eslint.config.js
import js from '@eslint/js';
import svelte from 'eslint-plugin-svelte';
import tsParser from 'typescript-eslint';
export default [
js.configs.recommended,
...svelte.configs['flat/recommended'],
{
languageOptions: {
parserOptions: {
extraFileExtensions: ['.svelte'],
},
},
},
];
Debugging und Performance
Svelte DevTools
Installieren Sie die Svelte DevTools-Browsererweiterung, um Komponentenhierarchien und -zustände zu inspizieren. Während Cursor den Editor hostet, zeigen die Browser-DevTools Laufzeitwerte, die für das Debugging von Runes entscheidend sind.
Source Maps in Produktion
SvelteKit generiert standardmäßig Source Maps. Bestätigen Sie in svelte.config.js:
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter(),
},
};
export default config;
Stellen Sie beim Deployment auf Serverless-Plattformen sicher, dass Ihr Adapter Source Maps korrekt behandelt. Die Adapter adapter-node und adapter-vercel verhalten sich unterschiedlich — lesen Sie die Adapter-Dokumentation, bevor Sie Source Maps in die Produktion senden.
Bundle-Analyse
Fügen Sie rollup-plugin-visualizer hinzu, um die Bundle-Größe zu inspizieren:
// vite.config.ts
import { sveltekit } from '@sveltejs/vite-plugin-sveltekit';
import { defineConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
sveltekit(),
visualizer({
filename: 'stats.html',
open: true,
gzipSize: true,
}),
],
});
Best Practices für Svelte in Cursor
Projektstruktur
src/
├── lib/
│ ├── components/
│ │ ├── Accordion.svelte
│ │ └── TextInput.svelte
│ ├── stores/
│ │ └── user.ts
│ ├── state/
│ │ └── counter.svelte.ts
│ └── utils/
├── routes/
│ ├── +layout.svelte
│ └── +page.svelte
└── app.html
Namenskonventionen für Runes-Dateien
Svelte 5 führt die .svelte.ts- (und .svelte.js-) Erweiterung für Module ein, die Runes außerhalb von Komponenten verwenden. Benennen Sie gemeinsame Zustandsdateien entsprechend:
// src/lib/state/cart.svelte.ts
export class CartState {
items = $state<{ id: number; qty: number }[]>([]);
get total() {
return this.items.reduce((sum, i) => sum + i.qty, 0);
}
add(id: number, qty = 1) {
this.items.push({ id, qty });
}
}
Das .svelte.ts-Suffix teilt dem Svelte-Compiler mit, Runes in normalen TypeScript-Modulen zu verarbeiten. Ohne es wird $state als undefinierte Funktion behandelt und der Build schlägt fehl.
Barrierefreiheit als Standard
Sveltes Compiler warnt vor fehlenden alt, aria-* und Label-Zuordnungen. Behandeln Sie diese Warnungen in CI als Fehler:
// svelte.config.js
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
const config = {
preprocess: vitePreprocess(),
kit: { adapter: adapter() },
compilerOptions: {
accessors: false,
},
};
export default config;
Häufige Fallstricke und wie man sie vermeidet
Verwendung veralteter Event-Syntax
Svelte 5 veraltet on:click zugunsten von onclick. Das Mischen beider funktioniert während der Übergangsphase, erzeugt aber Warnungen. Standardisieren Sie auf die neue Syntax:
<!-- Svelte 4 (veraltet) -->
<button on:click={handler}>Click</button>
<!-- Svelte 5 (empfohlen) -->
<button onclick={handler}>Click</button>
Props verändern
Props sind standardmäßig schreibgeschützt. Verwenden Sie $bindable für bidirektionale Bindung oder emitieren Sie Callbacks:
<script lang="ts">
interface Props {
value: string;
onChange?: (v: string) => void;
}
let { value, onChange }: Props = $props();
function handleInput(e: Event) {
const target = e.target as HTMLInputElement;
onChange?.(target.value);
}
</script>
<input value={value} oninput={handleInput} />
Übermäßige Verwendung von $effect
Effects sollten mit externen Systemen synchronisieren, keine Renderwerte berechnen. Wenn Sie feststellen, dass Sie $effect schreiben, um eine im Template verwendete Variable zu aktualisieren, wechseln Sie zu $derived:
<script lang="ts">
let a = $state(1);
let b = $state(2);
// FALSCH — Effect für abgeleiteten Wert
// let sum = $state(0);
// $effect(() => { sum = a + b; });
// RICHTIG — abgeleitet
let sum = $derived(a + b);
</script>
Effects, die von sich selbst abhängen, können Endlosschleifen erzeugen. Der Svelte-Compiler erkennt einige Fälle, aber stellen Sie immer sicher, dass die Abhängigkeiten Ihres Effects endlich und terminierend sind.
Zusammenfassung
Cursor ist eine leistungsstarke Umgebung für die Svelte- und SvelteKit-Entwicklung. Die offizielle Svelte-Erweiterung bietet typsichere Erstellung und Formatierung, Svelte 5 Runes bringen explizite und vorhersehbare Reaktivität, und SvelteKits dateibasiertes Routing mit typisierten load-Funktionen beschleunigt Full-Stack-Arbeit. Durch die Kombination dieser Tools mit Cursors KI-Assistent für Komponentengenerierung, Refactoring und Test-Scaffolding können Sie moderne Svelte-Anwendungen effizient erstellen. Beginnen Sie mit sv create, verankern Sie Ihre Prettier- und ESLint-Konfiguration, und lassen Sie Runes und KI das wiederholte Scaffolding übernehmen, während Sie sich auf die Lieferung eines polierten Benutzererlebnisses konzentrieren.