Retour au blog

Svelte 5 et Runes : La Révolution de la Réactivité en JavaScript en 2025

Salut HaWkers, Svelte 5 est arrivé avec un changement paradigmatique qui fait repenser à la communauté JavaScript comment nous abordons la réactivité dans les applications web. Les Runes représentent une nouvelle façon de penser l'état et la réactivité, et si vous ne les connaissez pas encore, vous passez à côté de l'une des innovations les plus intéressantes de l'écosystème frontend.

Vous êtes-vous déjà demandé pourquoi nous avons besoin de tant de hooks et de boilerplate pour gérer l'état en React ? Svelte 5 offre une alternative élégante.

Que Sont les Runes ?

Les Runes sont une nouvelle primitive de réactivité introduite dans Svelte 5. Ils remplacent le système de réactivité précédent basé sur les déclarations réactives ($:) par un modèle plus explicite et puissant.

Les Principales Runes

Liste des Runes disponibles :

  • $state - Déclare un état réactif
  • $derived - Calcule des valeurs dérivées
  • $effect - Exécute des side effects réactifs
  • $props - Reçoit les props dans les composants
  • $bindable - Props qui peuvent être two-way bound
  • $inspect - Debug des valeurs réactives

Comparaison : Svelte 4 vs Svelte 5

Voyons en pratique comment la syntaxe a changé et pourquoi c'est une amélioration significative.

État Simple

Svelte 4 (ancien) :

<script>
  let count = 0;

  function increment() {
    count += 1;
  }
</script>

<button on:click={increment}>
  Clics : {count}
</button>

Svelte 5 (Runes) :

<script>
  let count = $state(0);

  function increment() {
    count += 1;
  }
</script>

<button onclick={increment}>
  Clics : {count}
</button>

La différence peut sembler subtile, mais $state rend la réactivité explicite et prévisible.

Valeurs Dérivées

Svelte 4 (ancien) :

<script>
  let firstName = 'John';
  let lastName = 'Doe';

  // Déclaration réactive - peut être confuse
  $: fullName = `${firstName} ${lastName}`;
  $: greeting = `Bonjour, ${fullName} !`;
</script>

Svelte 5 (Runes) :

<script>
  let firstName = $state('John');
  let lastName = $state('Doe');

  // Beaucoup plus clair et prévisible
  let fullName = $derived(`${firstName} ${lastName}`);
  let greeting = $derived(`Bonjour, ${fullName} !`);
</script>

Effects (Side Effects)

Svelte 4 (ancien) :

<script>
  let count = 0;

  // Effect implicite - difficile de savoir quand il s'exécute
  $: {
    console.log(`Count a changé pour : ${count}`);
    localStorage.setItem('count', count);
  }
</script>

Svelte 5 (Runes) :

<script>
  let count = $state(0);

  // Effect explicite - clair quand et pourquoi il s'exécute
  $effect(() => {
    console.log(`Count a changé pour : ${count}`);
    localStorage.setItem('count', count.toString());
  });
</script>

Exemple Pratique : Todo App avec Runes

Construisons une application de tâches complète en utilisant les Runes :

<script>
  // État principal
  let todos = $state([]);
  let newTodo = $state('');
  let filter = $state('all'); // 'all' | 'active' | 'completed'

  // Valeurs dérivées
  let filteredTodos = $derived(() => {
    switch (filter) {
      case 'active':
        return todos.filter(t => !t.completed);
      case 'completed':
        return todos.filter(t => t.completed);
      default:
        return todos;
    }
  });

  let remainingCount = $derived(
    todos.filter(t => !t.completed).length
  );

  let allCompleted = $derived(
    todos.length > 0 && todos.every(t => t.completed)
  );

  // Fonctions d'action
  function addTodo() {
    if (newTodo.trim()) {
      todos.push({
        id: Date.now(),
        text: newTodo.trim(),
        completed: false
      });
      newTodo = '';
    }
  }

  function toggleTodo(id) {
    const todo = todos.find(t => t.id === id);
    if (todo) {
      todo.completed = !todo.completed;
    }
  }

  function removeTodo(id) {
    const index = todos.findIndex(t => t.id === id);
    if (index !== -1) {
      todos.splice(index, 1);
    }
  }

  function toggleAll() {
    const newState = !allCompleted;
    todos.forEach(t => t.completed = newState);
  }

  function clearCompleted() {
    todos = todos.filter(t => !t.completed);
  }

  // Effect pour la persistance
  $effect(() => {
    localStorage.setItem('todos', JSON.stringify(todos));
  });

  // Charger depuis localStorage à l'initialisation
  $effect(() => {
    const saved = localStorage.getItem('todos');
    if (saved) {
      todos = JSON.parse(saved);
    }
  });
</script>

<div class="todo-app">
  <h1>Mes Tâches</h1>

  <form onsubmit={(e) => { e.preventDefault(); addTodo(); }}>
    <input
      type="text"
      bind:value={newTodo}
      placeholder="Que faut-il faire ?"
    />
    <button type="submit">Ajouter</button>
  </form>

  {#if todos.length > 0}
    <div class="controls">
      <button onclick={toggleAll}>
        {allCompleted ? 'Décocher' : 'Cocher'} Toutes
      </button>

      <div class="filters">
        <button
          class:active={filter === 'all'}
          onclick={() => filter = 'all'}
        >
          Toutes
        </button>
        <button
          class:active={filter === 'active'}
          onclick={() => filter = 'active'}
        >
          Actives
        </button>
        <button
          class:active={filter === 'completed'}
          onclick={() => filter = 'completed'}
        >
          Complétées
        </button>
      </div>
    </div>

    <ul class="todo-list">
      {#each filteredTodos as todo (todo.id)}
        <li class:completed={todo.completed}>
          <input
            type="checkbox"
            checked={todo.completed}
            onchange={() => toggleTodo(todo.id)}
          />
          <span>{todo.text}</span>
          <button onclick={() => removeTodo(todo.id)}>X</button>
        </li>
      {/each}
    </ul>

    <footer>
      <span>{remainingCount} tâche(s) restante(s)</span>
      {#if todos.some(t => t.completed)}
        <button onclick={clearCompleted}>
          Effacer Complétées
        </button>
      {/if}
    </footer>
  {/if}
</div>

Pourquoi les Runes Sont Meilleures ?

1. Réactivité Explicite

En Svelte 4, toute variable let était automatiquement réactive, ce qui pouvait causer de la confusion :

<!-- Svelte 4 : Quand est-ce réactif ? -->
<script>
  let data = fetch('/api').then(r => r.json()); // Réactif ?
  let config = { theme: 'dark' }; // Réactif ?
  const MAX = 100; // Non réactif (const)
</script>

Avec les Runes, vous déclarez explicitement ce qui est réactif :

<!-- Svelte 5 : Clarté totale -->
<script>
  let data = $state(null); // Réactif
  let config = $state({ theme: 'dark' }); // Réactif
  const MAX = 100; // Non réactif (intentionnel)
</script>

2. Meilleur Support TypeScript

Les Runes fonctionnent parfaitement avec TypeScript :

<script lang="ts">
  interface Todo {
    id: number;
    text: string;
    completed: boolean;
  }

  let todos = $state<Todo[]>([]);
  let newTodo = $state('');

  // Types inférés correctement
  let completedCount = $derived(
    todos.filter(t => t.completed).length
  );
</script>

3. Réactivité Universelle

Les Runes fonctionnent en dehors des composants .svelte :

// stores/counter.svelte.ts
export function createCounter(initial = 0) {
  let count = $state(initial);

  return {
    get value() { return count; },
    increment() { count += 1; },
    decrement() { count -= 1; },
    reset() { count = initial; }
  };
}

// Dans n'importe quel composant
import { createCounter } from './stores/counter.svelte';

const counter = createCounter(10);

Comparaison avec React

Comparons la même fonctionnalité en React et Svelte 5 :

React (Hooks) :

import { useState, useMemo, useEffect } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  const [step, setStep] = useState(1);

  // useMemo pour les valeurs dérivées
  const doubled = useMemo(() => count * 2, [count]);

  // useEffect pour les side effects
  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

  return (
    <div>
      <p>Count: {count} (doubled: {doubled})</p>
      <input
        type="number"
        value={step}
        onChange={e => setStep(Number(e.target.value))}
      />
      <button onClick={() => setCount(c => c + step)}>
        Incrémenter de {step}
      </button>
    </div>
  );
}

Svelte 5 (Runes) :

<script>
  let count = $state(0);
  let step = $state(1);

  // Dérivé - sans tableau de dépendances
  let doubled = $derived(count * 2);

  // Effect - sans tableau de dépendances
  $effect(() => {
    document.title = `Count: ${count}`;
  });
</script>

<div>
  <p>Count: {count} (doubled: {doubled})</p>
  <input type="number" bind:value={step} />
  <button onclick={() => count += step}>
    Incrémenter de {step}
  </button>
</div>

Différences notables :

  • Pas de tableaux de dépendances (moins de bugs)
  • Syntaxe plus concise
  • Binding bidirectionnel natif
  • Moins de boilerplate

Performance de Svelte 5

Svelte 5 apporte des améliorations significatives de performance :

Benchmarks (js-framework-benchmark)

Métrique React 19 Vue 3.5 Svelte 5
Créer 1000 lignes 45ms 38ms 32ms
Mettre à jour 1000 lignes 38ms 32ms 28ms
Sélectionner ligne 3.2ms 2.8ms 2.1ms
Supprimer ligne 35ms 30ms 25ms
Créer 10000 lignes 450ms 380ms 320ms
Taille du bundle (gzip) 45Ko 35Ko 3Ko

Point fort : Svelte compile en JavaScript vanilla, résultant en des bundles significativement plus petits.

Migrer de Svelte 4 vers Svelte 5

Stratégie de Migration

  1. Mettez à jour Svelte : npm install svelte@5
  2. Le code existant fonctionne : Svelte 5 est rétrocompatible
  3. Migrez progressivement : Convertissez les composants un par un
  4. Utilisez le mode legacy : <svelte:options runes={false} />

Outil de Migration

Svelte offre un outil de migration automatique :

# Migrer les fichiers automatiquement
npx sv migrate svelte-5

# Vérifier les problèmes de compatibilité
npx svelte-check

Patterns de Migration

Stores vers Runes :

// Svelte 4 : Stores
import { writable, derived } from 'svelte/store';

export const count = writable(0);
export const doubled = derived(count, $count => $count * 2);

// Svelte 5 : Runes (les stores fonctionnent encore !)
// Mais les Runes sont recommandés pour le nouveau code
let count = $state(0);
let doubled = $derived(count * 2);

Quand Utiliser Svelte 5 ?

Utilisez Svelte 5 quand :

  • Vous démarrez un nouveau projet frontend
  • Vous avez besoin de performance maximale
  • Vous voulez moins de boilerplate
  • Vous préférez une syntaxe plus proche du JavaScript vanilla
  • Vous avez besoin de bundles plus petits

Considérez d'autres options quand :

  • L'équipe est très expérimentée en React/Vue
  • L'écosystème de bibliothèques est crucial
  • Vous avez besoin de SSR complexe (SvelteKit est encore plus jeune)

Conclusion

Svelte 5 avec les Runes représente une évolution significative dans la façon dont nous pensons la réactivité en JavaScript. La syntaxe explicite, le meilleur support TypeScript et la performance supérieure en font un choix attractif pour les nouveaux projets.

Si vous travaillez déjà avec React ou Vue, les concepts des Runes vous seront familiers, mais l'implémentation est plus élégante et directe. Cela vaut la peine d'expérimenter dans un projet personnel pour sentir la différence.

La communauté JavaScript continue d'innover, et des frameworks comme Svelte montrent qu'il y a encore beaucoup d'espace pour des améliorations dans la façon dont nous construisons des applications web.

Si vous voulez explorer plus sur les frameworks JavaScript modernes, je recommande de jeter un œil à l'article sur Svelte vs Vue vs React : Comparatif des Frameworks 2025 où nous comparons les principales options du marché.

C'est parti ! 🦅

📚 Vous Voulez Approfondir Vos Connaissances en JavaScript ?

Cet article a couvert Svelte 5 et les Runes, mais il y a beaucoup plus à explorer dans le monde du développement moderne.

Les développeurs qui investissent dans des connaissances solides et structurées ont tendance à avoir plus d'opportunités sur le marché.

Matériel d'Étude Complet

Si vous voulez maîtriser JavaScript du niveau débutant à avancé, j'ai préparé un guide complet :

Options d'investissement :

  • 1x de 9,90€ par carte
  • ou 9,90€ comptant

👉 Découvrir le Guide JavaScript

💡 Matériel mis à jour avec les meilleures pratiques du marché

Commentaires (0)

Cet article n'a pas encore de commentaires. Soyez le premier!

Ajouter des commentaires