• Sobre mí
  • Proyectos
  • Contacto
  • Blog

Apasionado por el desarrollo web y el diseño de interfaces intuitivas.

Cada error es un paso. Cada línea, un aprendizaje.

📍   Salou, Tarragona, España ✉️   [email protected] 📞   +34 641932611

Curriculum

Ver mi curriculum completo

Mapa del sitio

  • Inicio
  • Blog
  • Proyectos
  • Sobre mi
  • Mi misión
  • Sitemap

Contácto

  • Página de contácto
  • LinkedIn
  • GitHub

2026 Frankuxui - Todos los derechos reservados

  • Términos y condiciones
  • Política de privacidad
  • Política de cookies
Rocky mountain peaks with traces of snow beneath a pale sky
Fotografía de Unsplash creada por Avatar del usuario Roman Vasylovskyi
  • Svelte

Articulo publicado el 16 de agosto de 2026

Por Frank Esteban Isdray Junco

Runes en Svelte 5: guia practica de $state, $derived y $effect

Como sustituir let reactivo y las declaraciones $: por un modelo de reactividad explicito y mas facil de depurar

Svelte 5 cambio la forma de declarar estado reactivo. En versiones anteriores, una variable declarada con let dentro de un componente era reactiva de forma implicita, y las dependencias derivadas se calculaban con la sintaxis $: etiqueta. Ese modelo funcionaba, pero tenia un problema conocido: la reactividad dependia del compilador detectando asignaciones, no de una declaracion explicita en el codigo. Cuanto mas grande era el componente, mas dificil era saber, solo leyendo el archivo, que variables eran reactivas y por que.

Las runes resuelven ese problema haciendo la reactividad explicita. $state, $derived y $effect son funciones especiales que el compilador de Svelte reconoce, y que declaran con claridad la intencion: esto es estado, esto se deriva de otro estado, esto es un efecto secundario.

Que es una rune y por que existen

Una rune no es una funcion normal de JavaScript, aunque se escribe con sintaxis de funcion. Es una instruccion que el compilador de Svelte interpreta en tiempo de compilacion para generar el codigo reactivo correspondiente. Por eso solo pueden usarse dentro de archivos .svelte y .svelte.js o .svelte.ts, y no dentro de modulos JavaScript normales sin esa extension.

El objetivo de las runes es que la reactividad sea visible en el propio codigo, sin depender de que el desarrollador recuerde que let es especial dentro de un componente Svelte. Esto tiene una ventaja practica directa: un desarrollador que llega de React, Vue o de JavaScript vanilla puede leer $state(0) y entender inmediatamente que ese valor es observado por el sistema de reactividad, sin conocer las reglas implicitas del compilador anterior.

$state: declarar valores reactivos

$state reemplaza a let como forma de declarar una variable reactiva. Cuando el valor cambia, cualquier parte de la interfaz que dependa de el se actualiza.

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

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

Lo relevante aqui no es la sintaxis en si, sino el comportamiento con objetos y arrays. $state no solo marca la variable como reactiva: envuelve objetos y arrays en un proxy reactivo, de forma que mutaciones internas (como push en un array o la asignacion de una propiedad) tambien disparan actualizaciones, sin necesidad de reasignar la variable completa.

svelte
<script>
  let user = $state({ name: "Marta", age: 29 });

  function cumplirAnios() {
    user.age++;
  }
</script>

<p>{user.name} tiene {user.age} años</p>
<button onclick={cumplirAnios}>Cumplir años</button>

Este comportamiento es distinto al de React, donde mutar un objeto de estado directamente no dispara un re-render y hay que crear una copia nueva. En Svelte 5, gracias al proxy que envuelve $state, la mutacion directa es la forma esperada de trabajar.

$derived: valores calculados a partir de otro estado

$derived sustituye a la antigua sintaxis $: variable = expresion. Declara un valor que se recalcula automaticamente cuando cambian sus dependencias.

svelte
<script>
  let precio = $state(20);
  let cantidad = $state(3);

  const total = $derived(precio * cantidad);
</script>

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

La expresion dentro de $derived debe ser una expresion pura, sin efectos secundarios como llamadas a fetch, escritura en el DOM o console.log con fines de depuracion que dependan del orden de ejecucion. Si el calculo necesita varias sentencias, o logica intermedia con condicionales, la forma recomendada es $derived.by, que acepta una funcion:

svelte
<script>
  let items = $state([{ precio: 10 }, { precio: 25 }, { precio: 5 }]);

  const total = $derived.by(() => {
    let suma = 0;
    for (const item of items) {
      suma += item.precio;
    }
    return suma;
  });
</script>

Un criterio practico para decidir entre $derived y $effect es este: si el resultado es un valor que la interfaz va a mostrar o que otra parte del codigo va a leer, casi siempre es $derived. Si lo que necesitas es ejecutar una accion (una peticion de red, escribir en localStorage, sincronizar con una libreria externa), entonces es un efecto.

$effect: efectos secundarios ligados a la reactividad

$effect ejecuta una funcion cuando el componente se monta y vuelve a ejecutarla cada vez que cambian los valores reactivos que lee dentro de su cuerpo. No devuelve un valor para la interfaz: su proposito es ejecutar una accion.

svelte
<script>
  let query = $state("");
  let resultados = $state([]);

  $effect(() => {
    if (query.length === 0) {
      resultados = [];
      return;
    }

    const controller = new AbortController();

    fetch(`/api/buscar?q=${query}`, { signal: controller.signal })
      .then((res) => res.json())
      .then((data) => (resultados = data));

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

Este ejemplo tambien muestra un detalle importante: si la funcion pasada a $effect devuelve otra funcion, esa funcion de retorno actua como limpieza (cleanup) y se ejecuta antes de la siguiente ejecucion del efecto o cuando el componente se destruye. Es el mismo patron que el array de dependencias y la funcion de limpieza en useEffect de React, pero sin necesidad de declarar manualmente las dependencias: Svelte las detecta automaticamente a partir de que variables reactivas se leen dentro del efecto.

Segun la documentacion oficial de Svelte, los efectos se ejecutan despues de que el componente se ha montado en el DOM, en una microtarea posterior a los cambios de estado. Esto significa que no deben usarse para calcular valores derivados, ya que ese es el proposito de $derived. Fuente: Svelte Docs — $effect.

Casos de uso y cuando evitar cada rune

Algunos escenarios habituales ayudan a decidir que rune corresponde a cada situacion:

  • Un contador, un formulario controlado o cualquier valor que cambia por interaccion del usuario: $state.
  • Un total, un filtro aplicado sobre una lista, o un booleano calculado a partir de otros valores (por ejemplo, esValido = $derived(nombre.length > 0 && email.includes("@"))): $derived.
  • Sincronizar el titulo del documento (document.title), inicializar una libreria de terceros, suscribirse a un evento del navegador o hacer logging con fines de depuracion: $effect.
  • Props recibidas por un componente hijo que necesitan ser reactivas: la rune $props(), que sustituye a export let y que conviene documentar aparte porque tiene reglas propias sobre destructuracion y valores por defecto.

Un error frecuente al migrar codigo desde el modelo anterior es usar $effect para calcular un valor derivado, asignando el resultado a otra variable de estado dentro del efecto:

svelte
<script>
  // Evitar: usar un efecto para derivar un valor
  let total = $state(0);
  $effect(() => {
    total = precio * cantidad;
  });
</script>

Este patron funciona, pero introduce una vuelta adicional del ciclo reactivo, complica el orden de ejecucion y dificulta razonar sobre de donde viene el valor. La alternativa correcta es declarar total directamente como $derived(precio * cantidad), sin pasar por un efecto.

Migrar codigo existente sin romper el componente

Al migrar un componente de Svelte 4 a runes, conviene avanzar por partes en lugar de reescribir todo el archivo de golpe:

  1. Sustituye cada let reactivo (usado en el marcado o leido dentro de $:) por $state.
  2. Sustituye cada $: variable = expresion pura por $derived, y cada $: { ... } con varias sentencias por $derived.by o por $effect, segun si produce un valor o ejecuta una accion.
  3. Revisa los export let de props y migralos a $props() en un paso separado, ya que su sintaxis de destructuracion y valores por defecto difiere de la anterior.
  4. Ejecuta el proyecto con el modo de desarrollo activo y observa la consola: el compilador de Svelte 5 avisa cuando detecta patrones ambiguos, como leer y escribir la misma variable de estado dentro de un mismo $effect sin condicion de salida.

Este orden reduce el riesgo de introducir bucles de reactividad, que en el modelo anterior tambien podian ocurrir, pero que con runes son mas faciles de detectar porque cada bloque tiene una responsabilidad unica: guardar estado, derivar un valor o ejecutar un efecto.

Conclusion

Las runes no cambian la filosofia de Svelte de compilar reactividad en tiempo de build, pero si cambian como se expresa esa reactividad en el codigo. $state deja claro que variable es observada, $derived deja claro que valor depende de otro sin ejecutar nada por si mismo, y $effect queda reservado para acciones con efectos secundarios reales. Para quien mantiene componentes grandes, esa separacion explicita facilita revisar codigo ajeno, depurar actualizaciones inesperadas y decidir, sin ambigüedad, en que rune debe vivir cada pieza de logica.

En este artículo

  1. Que es una rune y por que existen
  2. $state: declarar valores reactivos
  3. $derived: valores calculados a partir de otro estado
  4. $effect: efectos secundarios ligados a la reactividad
  5. Casos de uso y cuando evitar cada rune
  6. Migrar codigo existente sin romper el componente
  7. Conclusion