Développement Svelte et SvelteKit dans Cursor : Un Guide Complet

Svelte est un framework UI basé sur un compilateur qui envoie un minimum de JavaScript au navigateur, et SvelteKit est son framework d'application full-stack. Svelte 5 a introduit les runes, un nouveau modèle de réactivité plus explicite et plus puissant que la réactivité basée sur let précédente. Cursor, avec sa fondation compatible VS Code et son assistant IA intégré, est un excellent éditeur pour le travail Svelte — il comprend les fichiers .svelte, fournit un riche IntelliSense et peut générer des composants à partir de prompts en langage naturel.
Ce guide couvre le flux de travail complet de développement Svelte dans Cursor : configuration de la chaîne d'outils, maîtrise des runes Svelte 5, construction d'applications SvelteKit avec routage basé sur les fichiers, gestion de l'état partagé avec les stores, et exploitation de l'IA pour générer et refactorer des composants.
Configuration de l'environnement de développement Svelte dans Cursor
Prérequis
Svelte 5 et SvelteKit nécessitent Node.js 18 ou supérieur. Vérifiez votre installation :
node --version
npm --version
Si vous avez besoin d'une version plus récente, utilisez fnm ou nvm :
fnm install 20
fnm use 20
Le terminal intégré de Cursor (Ctrl+`) mémorise votre répertoire de travail courant, vous pouvez donc scaffolder un projet et ouvrir immédiatement des fichiers sans changer de contexte.
Installation de l'extension Svelte
Cursor s'appuie sur l'extension officielle Svelte pour le support linguistique. Installez-la depuis la vue Extensions (Ctrl+Shift+X) :
- Recherchez "Svelte".
- Installez l'extension publiée par l'équipe Svelte.
- Rechargez Cursor si demandé.
L'extension fournit :
- La coloration syntaxique pour les fichiers
.svelte. - Les diagnostics et la vérification des types via
svelte-language-server. - Le formatage via Prettier avec le package
prettier-plugin-svelte. - L'IntelliSense pour les imports de composants et les props.
Si vous utilisiez précédemment une extension Svelte communautaire, désactivez-la avant d'installer l'officielle. Les extensions conflictuelles provoquent des diagnostics dupliqués et un formatage peu fiable.
Scaffolding d'un projet SvelteKit
Utilisez le CLI officiel sv pour créer une nouvelle application SvelteKit :
npx sv create my-svelte-app
L'invite interactive propose des préréglages :
? 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
Ouvrez le projet dans Cursor :
cd my-svelte-app
cursor .
npm install
npm run dev
Le serveur de développement démarre sur http://localhost:5173.
Vous pouvez ajouter des fonctionnalités ultérieurement sans recréer le projet. La commande sv add installe des intégrations telles que Tailwind CSS, Storybook ou Drizzle sur une application existante :
npx sv add tailwindcss
npx sv add storybook
Maîtriser les Runes Svelte 5
Qu'est-ce que les Runes ?
Les runes sont des fonctions spéciales reconnues par le compilateur introduites dans Svelte 5. Elles remplacent la réactivité implicite de Svelte 4 par un modèle explicite et prévisible. Les runes principales sont :
$state— déclare un état réactif.$derived— déclare une valeur calculée à partir d'un autre état réactif.$effect— exécute des effets de bord lorsque les dépendances changent.$props— déclare les props du composant.$bindable— marque une prop comme liable bidirectionnellement.
Déclarer l'état avec $state
<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>
Dans Svelte 5, les gestionnaires d'événements utilisent l'attribut onclick (minuscules, sans préfixe on:) au lieu de l'ancien on:click. La nouvelle syntaxe est plus proche des attributs DOM standards et fonctionne mieux avec TypeScript.
Valeurs dérivées avec $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>
Effets de bord avec $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 s'exécute après que le DOM a été mis à jour. Ne l'utilisez pas pour calculer des valeurs qui affectent le rendu — utilisez $derived à la place. Les effects servent à synchroniser avec des systèmes externes (API, localStorage, bibliothèques tierces).
Props de composant avec $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>
Liaison bidirectionnelle avec $bindable
<!-- TextInput.svelte -->
<script lang="ts">
interface Props {
value?: string;
}
let { value = $bindable('') }: Props = $props();
</script>
<input bind:value />
Utilisation dans un parent :
<script lang="ts">
import TextInput from './TextInput.svelte';
let name = $state('');
</script>
<TextInput bind:value={name} />
<p>Hello, {name}!</p>
$bindable est le remplaçant de Svelte 5 pour le pattern export let + bind:value. Il rend le contrat de liaison bidirectionnelle explicite dans la signature de type du composant enfant.
Construction d'applications SvelteKit
Routage basé sur les fichiers
SvelteKit utilise le routage basé sur les fichiers dans le répertoire src/routes. Chaque fichier +page.svelte devient une page, et les fichiers +layout.svelte enveloppent les pages imbriquées.
src/routes/
├── +layout.svelte
├── +page.svelte → /
├── about/
│ └── +page.svelte → /about
├── blog/
│ ├── +page.svelte → /blog
│ └── [slug]/
│ └── +page.svelte → /blog/:slug
└── +layout.svelte
Charger des données avec +page.server.ts
// 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 };
};
Consommer les données dans la page :
<!-- 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>
Le module $types est généré automatiquement par SvelteKit. Il vous donne des fonctions load et des props data entièrement typées. L'IntelliSense de Cursor lit ces types automatiquement, vous obtenez donc l'autocomplétion pour data.post.title.
Actions de formulaire
SvelteKit gère les soumissions de formulaire avec des actions définies dans +page.server.ts :
// 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');
},
};
La page correspondante utilise l'action enhance pour l'amélioration progressive :
<!-- 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>
Les fichiers +page.server.ts côté serveur ne s'exécutent que sur le serveur. N'y importez jamais d'API réservées au navigateur (comme localStorage), et ne placez jamais de secrets dans +page.ts (le load universel), qui peut aussi s'exécuter dans le navigateur.
Layouts et groupes de layout
<!-- 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>
Dans Svelte 5, le snippet children remplace la syntaxe <slot /> dépréciée.
Gestion de l'état partagé avec les Stores
Bases des Svelte Stores
Même avec les runes, les stores Svelte restent utiles pour l'état inter-composants. Un store est tout objet avec une méthode subscribe.
// lib/stores/user.ts
import { writable } from 'svelte/store';
interface User {
id: string;
name: string;
}
export const currentUser = writable<User | null>(null);
Consommer le store avec le préfixe $ :
<script lang="ts">
import { currentUser } from '$lib/stores/user';
</script>
{#if $currentUser}
<p>Welcome, {$currentUser.name}</p>
{:else}
<p>Please log in.</p>
{/if}
Stores Readable et Derived
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')
);
Combiner Runes et Stores
Vous pouvez utiliser des stores dans $effect pour faire le pont entre le code legacy et les runes :
<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>
Pour les nouveaux projets, préférez un pattern d'état partagé basé sur les runes utilisant une classe avec des champs $state et l'API context de Svelte. Cela évite le boilerplate des stores tout en conservant la réactivité :
// lib/state/counter.svelte.ts
export class CounterState {
count = $state(0);
increment() {
this.count++;
}
}
Génération de composants Svelte assistée par IA
Générer des composants à partir de prompts
L'IA de Cursor excelle dans la production de composants Svelte 5 avec des runes. Ouvrez un chat (Ctrl+Shift+P → "Cursor: New Chat") ou utilisez les éditions en ligne (Ctrl+K) et décrivez ce dont vous avez besoin :
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 génère un fichier .svelte complet. Révisez la sortie, puis itérez — demandez-lui d'ajouter une prop allowMultiple ou des transitions animées.
Migration de Svelte 4 vers Svelte 5
Si vous maintenez un codebase plus ancien, Cursor peut migrer les composants vers la syntaxe des runes. Sélectionnez le fichier, appuyez sur Ctrl+K, et saisissez :
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.
La migration des runes modifie le timing de réactivité de manière subtile. Après le refactoring, exécutez votre suite de tests et vérifiez manuellement tout composant qui utilise des hooks de cycle de vie ou des watchers, car $effect se déclenche dans des conditions différentes de l'ancien afterUpdate.
Génération de tests de bout en bout
Associez Playwright à des tests générés par IA :
// 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();
});
Demandez à Cursor : "Generate Playwright tests for Accordion.svelte covering expand/collapse and keyboard navigation."
Configuration de Cursor pour les projets Svelte
Paramètres de l'espace de travail
Créez .vscode/settings.json pour standardiser le formatage dans toute l'équipe :
{
"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 avec le plugin Svelte
npm install -D prettier prettier-plugin-svelte
.prettierrc:
{
"singleQuote": true,
"semi": true,
"plugins": ["prettier-plugin-svelte"],
"overrides": [
{
"files": "*.svelte",
"options": { "parser": "svelte" }
}
]
}
Prettier formate les blocs <script> et <style> mais laisse intentionnellement le balisage de template Svelte seul pour éviter de casser les directives. Pour le formatage des templates, comptez sur le formateur intégré de l'extension Svelte.
Configuration ESLint
// 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'],
},
},
},
];
Débogage et performance
Svelte DevTools
Installez l'extension de navigateur Svelte DevTools pour inspecter la hiérarchie des composants et l'état. Bien que Cursor héberge l'éditeur, les DevTools du navigateur affichent les valeurs d'exécution qui sont critiques pour le débogage des runes.
Source Maps en production
SvelteKit génère des source maps par défaut. Confirmez dans 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;
Lors du déploiement sur des plateformes serverless, vérifiez que votre adapter gère correctement les source maps. Les adapters adapter-node et adapter-vercel se comportent différemment — consultez la documentation de l'adapter avant d'envoyer des source maps en production.
Analyse du bundle
Ajoutez rollup-plugin-visualizer pour inspecter la taille du bundle :
// 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,
}),
],
});
Bonnes pratiques pour Svelte dans Cursor
Structure du projet
src/
├── lib/
│ ├── components/
│ │ ├── Accordion.svelte
│ │ └── TextInput.svelte
│ ├── stores/
│ │ └── user.ts
│ ├── state/
│ │ └── counter.svelte.ts
│ └── utils/
├── routes/
│ ├── +layout.svelte
│ └── +page.svelte
└── app.html
Conventions de nommage pour les fichiers Runes
Svelte 5 introduit l'extension .svelte.ts (et .svelte.js) pour les modules qui utilisent des runes en dehors des composants. Nommez les fichiers d'état partagé en conséquence :
// 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 });
}
}
Le suffixe .svelte.ts indique au compilateur Svelte de traiter les runes dans les modules TypeScript ordinaires. Sans lui, $state est traité comme une fonction non définie et la build échoue.
Accessibilité par défaut
Le compilateur de Svelte avertit sur les alt manquants, les aria-* et les associations de labels. Traitez ces avertissements comme des erreurs en CI :
// 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;
Pièges courants et comment les éviter
Utiliser la syntaxe d'événement legacy
Svelte 5 déprécie on:click au profit de onclick. Mélanger les deux fonctionne pendant la transition mais produit des avertissements. Standardisez sur la nouvelle syntaxe :
<!-- Svelte 4 (déprécié) -->
<button on:click={handler}>Click</button>
<!-- Svelte 5 (recommandé) -->
<button onclick={handler}>Click</button>
Modifier les Props
Les props sont en lecture seule par défaut. Utilisez $bindable pour la liaison bidirectionnelle ou émettez des 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} />
Surutilisation de $effect
Les effects doivent se synchroniser avec des systèmes externes, pas calculer des valeurs de rendu. Si vous vous trouvez en train d'écrire $effect pour mettre à jour une variable utilisée dans le template, passez à $derived :
<script lang="ts">
let a = $state(1);
let b = $state(2);
// FAUX — effect pour une valeur dérivée
// let sum = $state(0);
// $effect(() => { sum = a + b; });
// CORRECT — dérivé
let sum = $derived(a + b);
</script>
Les effects qui dépendent d'eux-mêmes peuvent créer des boucles infinies. Le compilateur Svelte détecte certains cas, mais vérifiez toujours que les dépendances de votre effect sont finies et terminantes.
Résumé
Cursor est un environnement puissant pour le développement Svelte et SvelteKit. L'extension officielle Svelte fournit une création type-safe et un formatage, les runes Svelte 5 apportent une réactivité explicite et prévisible, et le routage basé sur les fichiers de SvelteKit avec des fonctions load typées accélère le travail full-stack. En associant ces outils à l'assistant IA de Cursor pour la génération de composants, le refactoring et le scaffolding de tests, vous pouvez construire des applications Svelte modernes efficacement. Commencez avec sv create, verrouillez votre configuration Prettier et ESLint, et laissez les runes et l'IA gérer le scaffolding répétitif pendant que vous vous concentrez sur la livraison d'une expérience utilisateur soignée.