← inicio

Svelte 5 y las Runas: reactividad sin magia

Svelte siempre fue el framework que te hacía sentir que escribir UI era fácil. El compilador hacía magia: declarabas una variable y era reactiva sin que tú hicieras nada. El problema es que la magia, cuando falla, es imposible de debuguear.

Svelte 5 introduce las Runas: funciones explícitas que declaran reactividad. Adiós magia, adiós sorpresas.

$state: la reactividad explícita

En Svelte 4:

<script>
  let count = 0; // mágicamente reactivo
</script>

En Svelte 5:

<script>
  import { useState } from 'svelte';
  let count = $state(0);
</script>

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

La diferencia: $state() es una señal (signal). Sabes exactamente qué es reactivo y qué no. El compilador no tiene que adivinar.

$derived: valores calculados

<script>
  let items = $state([]);
  let total = $derived(items.reduce((sum, i) => sum + i.price, 0));
</script>

<p>Total: {total}€</p>

Cada vez que items cambia, total se recalcula. Sin suscripciones manuales, sin useMemo, sin nada. Es una derivación pura.

$effect: efectos con cleanup

<script>
  let query = $state('');

  $effect(() => {
    const controller = new AbortController();

    fetch(`/api/search?q=${query}`, { signal: controller.signal })
      .then(r => r.json())
      .then(data => results = data);

    return () => controller.abort();
  });
</script>

La función que devuelve $effect es el cleanup. Se ejecuta antes de cada re-ejecución y al desmontar el componente. Nada de onDestroy separado.

Reactividad profunda

$state es profundo por defecto. Si tienes un objeto anidado y modificas una propiedad, el cambio se propaga:

<script>
  let form = $state({
    name: '',
    address: {
      city: '',
      zip: ''
    }
  });
</script>

<!--Modificar form.address.city dispara re-render -->
<input bind:value={form.address.city} />

No necesitas spread operators ni trucos. Svelte 5 trackea las mutaciones dentro de objetos $state de forma granular.

Snippets: la nueva forma de reutilizar UI

Svelte 5 introduce snippets — bloques de markup reutilizables dentro de un componente:

{#snippet card(title, content)}
  <div class="card">
    <h3>{title}</h3>
    <p>{content}</p>
  </div>
{/snippet}

{@render card('Hola', 'Este es el contenido')}
{@render card('Otro', 'Más contenido')}

Los snippets son más ligeros que los componentes: no crean un nuevo scope, no tienen lifecycle. Son trozos de UI que puedes invocar como funciones.

Mi veredicto

Las Runas hacen que Svelte 5 sea más predecible. Ya no hay reglas ocultas sobre qué variables son reactivas y cuáles no. Es explícito. Y el hecho de que la reactividad profunda funcione sin spread operators es un win enorme para formularios y estado anidado.

Lo que pierde en magia, lo gana en confianza. Y cuando debugueas a las 3 de la mañana, confianza es todo lo que quieres.