💻 Développement

dev-svelte-guide

Développement d'applications Svelte et SvelteKit avec réactivité native, stores, SSR, routing et transitions.

⚡ Installation & lancement en 1 commande

Copiez-collez dans votre terminal : le skill s'installe dans ~/.claude/skills et Claude Code se lance directement dessus.

macOS / Linux
curl -fsSL https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.sh | sh -s -- dev-svelte-guide --launch
Windows (PowerShell)
iex "& { $(iwr -useb https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.ps1) } dev-svelte-guide -Launch"

🚀 Déjà installé ?

claude "/dev-svelte-guide"

Ou tapez /dev-svelte-guide dans une session Claude Code, ou décrivez simplement votre besoin — le skill se déclenche automatiquement via le skill-router.

🔑 Déclencheurs automatiques

Le skill s'active automatiquement quand votre demande contient :

SvelteSvelteKitstore Svelteréactivité Svelte$:svelte:component

📦 Installation manuelle

git clone https://github.com/khalilbenaz/claude-skills-collection.git cp -r claude-skills-collection/skills/dev-svelte-guide ~/.claude/skills/

Payload du plugin : skills/dev-svelte-guide · source éditable : dev-skills/svelte-guide

📖 Manuel

Guide Svelte / SvelteKit

Critères de décision : Svelte vs SvelteKit

BesoinSolution
Composant UI isolé, widget embarquéSvelte seul, build library
SPA avec routing côté clientSvelteKit + adapter-static
Site statique / blog / docsSvelteKit + prerender + adapter-static
Application full-stack (API + DB + auth)SvelteKit + adapter-node ou adapter-vercel
SSR obligatoire (SEO, données fraîches)SvelteKit, SSR activé par défaut

Workflow

1. Initialiser le projet

# SvelteKit (recommandé)
npx sv create my-app
cd my-app && npm install
npm run dev

# Svelte seul (lib ou widget)
npx degit sveltejs/template my-widget

Choisir TypeScript dès le début — la migration ultérieure est coûteuse.

2. Structurer les routes SvelteKit

src/
  routes/
    +layout.svelte          # layout racine (nav, providers)
    +layout.server.ts       # load() partagé (session, user)
    +page.svelte            # page /
    blog/
      +page.svelte          # /blog
      [slug]/
        +page.svelte        # /blog/:slug
        +page.server.ts     # load({ params }) côté serveur
  lib/
    components/             # composants réutilisables
    stores/                 # stores partagés
    utils/                  # helpers purs
  app.html                  # template HTML racine

Règle : tout ce qui touche DB, secrets ou sessions va dans +page.server.ts ou +server.ts, jamais dans +page.ts (exécuté côté client aussi).

3. Réactivité — Svelte 4 vs Svelte 5

Svelte 4 (legacy)

<script>
  let count = 0;
  $: doubled = count * 2;
  $: if (count > 10) console.log('high');
</script>

Svelte 5 — Runes (recommandé pour tout nouveau projet)

<script>
  let count = $state(0);
  const doubled = $derived(count * 2);

  $effect(() => {
    if (count > 10) console.log('high');
    return () => { /* cleanup */ };
  });
</script>

<button onclick={() => count++}>{count} × 2 = {doubled}</button>

Avantages runes : réactivité fine (pas de réexécution du composant entier), cleanup explicite, lisibilité accrue, compatible composants universels.

4. Stores — état partagé

// src/lib/stores/cart.ts
import { writable, derived } from 'svelte/store';

const items = writable<CartItem[]>([]);

export const cart = {
  subscribe: items.subscribe,
  add: (item: CartItem) => items.update(i => [...i, item]),
  remove: (id: string) => items.update(i => i.filter(x => x.id !== id)),
  clear: () => items.set([]),
};

export const total = derived(items, $items =>
  $items.reduce((sum, i) => sum + i.price, 0)
);
<!-- Auto-souscription avec $store -->
<script>
  import { cart, total } from '$lib/stores/cart';
</script>
<p>Total : {$total} €</p>

Préférer le context API (setContext/getContext) quand le store n'est utile qu'à un sous-arbre — évite les fuites entre utilisateurs en SSR.

5. Data loading et form actions

// src/routes/products/[id]/+page.server.ts
import type { PageServerLoad, Actions } from './$types';
import { error, fail, redirect } from '@sveltejs/kit';

export const load: PageServerLoad = async ({ params, locals }) => {
  const product = await db.product.findUnique({ where: { id: params.id } });
  if (!product) error(404, 'Produit introuvable');
  return { product };
};

export const actions: Actions = {
  addToCart: async ({ request, locals }) => {
    const data = await request.formData();
    const qty = Number(data.get('qty'));
    if (!qty || qty < 1) return fail(422, { error: 'Quantité invalide' });
    await cartService.add(locals.user.id, params.id, qty);
    redirect(303, '/cart');
  }
};
<!-- +page.svelte -->
<script>
  import { enhance } from '$app/forms';
  let { data, form } = $props();
</script>

<h1>{data.product.name}</h1>
{#if form?.error}<p class="error">{form.error}</p>{/if}

<form method="POST" action="?/addToCart" use:enhance>
  <input name="qty" type="number" value="1" min="1" />
  <button>Ajouter au panier</button>
</form>

use:enhance : soumission progressive — fonctionne sans JS, AJAX avec JS.

6. Transitions et animations

<script>
  import { fade, fly, slide } from 'svelte/transition';
  import { flip } from 'svelte/animate';
  let visible = $state(true);
  let items = $state(['a', 'b', 'c']);
</script>

{#if visible}
  <div transition:fly={{ y: 20, duration: 300 }}>Contenu</div>
{/if}

{#each items as item (item)}
  <div animate:flip={{ duration: 250 }}>{item}</div>
{/each}

Pour les transitions de page (SvelteKit 2+) :

// src/hooks.client.ts
import { onNavigate } from '$app/navigation';
onNavigate(navigation => {
  if (!document.startViewTransition) return;
  return new Promise(resolve => {
    document.startViewTransition(async () => {
      resolve();
      await navigation.complete;
    });
  });
});

7. Composants avancés

<!-- Slot nommé -->
<Card>
  <svelte:fragment slot="header">Titre</svelte:fragment>
  Contenu par défaut
</Card>

<!-- Composant dynamique -->
<svelte:component this={selectedComponent} {props} />

<!-- Context API (évite prop drilling) -->
<script>
  import { setContext, getContext } from 'svelte';
  setContext('theme', { primary: '#3b82f6' });
  // Dans un enfant :
  const theme = getContext('theme');
</script>

8. Tester

npm install -D @testing-library/svelte vitest jsdom
// src/lib/Counter.test.ts
import { render, fireEvent } from '@testing-library/svelte';
import Counter from './Counter.svelte';

test('incrémente au clic', async () => {
  const { getByRole } = render(Counter);
  const btn = getByRole('button');
  await fireEvent.click(btn);
  expect(btn.textContent).toBe('1');
});

Pour les routes SvelteKit, utiliser Playwright pour les tests E2E :

npx playwright test

9. Déployer

# Node.js (serveur autonome)
npm install -D @sveltejs/adapter-node

# Static (Netlify, GitHub Pages)
npm install -D @sveltejs/adapter-static

# Vercel / Cloudflare (auto-detect)
npm install -D @sveltejs/adapter-vercel
npm install -D @sveltejs/adapter-cloudflare
// svelte.config.js
import adapter from '@sveltejs/adapter-node';
export default { kit: { adapter: adapter() } };

Prerender sélectif :

// +page.ts
export const prerender = true;   // page statique
export const ssr = false;        // SPA shell uniquement
export const csr = false;        // HTML pur sans hydratation

Garde-fous / Anti-patterns

Bonnes pratiques 2026