Voltar para o Blog

Svelte 5 e Runes: A Revolucao da Reatividade no JavaScript em 2025

Ola HaWkers, o Svelte 5 chegou com uma mudanca paradigmatica que esta fazendo a comunidade JavaScript repensar como abordamos reatividade em aplicacoes web. Os Runes representam uma nova forma de pensar sobre estado e reatividade, e se voce ainda nao conhece, esta perdendo uma das inovacoes mais interessantes do ecossistema frontend.

Voce ja se perguntou por que precisamos de tantos hooks e boilerplate para gerenciar estado em React? O Svelte 5 oferece uma alternativa elegante.

O Que Sao Runes?

Runes sao uma nova primitiva de reatividade introduzida no Svelte 5. Eles substituem o sistema de reatividade anterior baseado em declaracoes reativas ($:) por um modelo mais explicito e poderoso.

Os Principais Runes

Lista de Runes disponiveis:

  • $state - Declara estado reativo
  • $derived - Computa valores derivados
  • $effect - Executa side effects reativos
  • $props - Recebe props em componentes
  • $bindable - Props que podem ser two-way bound
  • $inspect - Debug de valores reativos

Comparacao: Svelte 4 vs Svelte 5

Vamos ver na pratica como a sintaxe mudou e por que e uma melhoria significativa.

Estado Simples

Svelte 4 (antigo):

<script>
  let count = 0;

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

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

Svelte 5 (Runes):

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

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

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

A diferenca parece sutil, mas o $state torna a reatividade explicita e previsivel.

Valores Derivados

Svelte 4 (antigo):

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

  // Declaracao reativa - pode ser confusa
  $: fullName = `${firstName} ${lastName}`;
  $: greeting = `Ola, ${fullName}!`;
</script>

Svelte 5 (Runes):

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

  // Muito mais claro e previsivel
  let fullName = $derived(`${firstName} ${lastName}`);
  let greeting = $derived(`Ola, ${fullName}!`);
</script>

Effects (Side Effects)

Svelte 4 (antigo):

<script>
  let count = 0;

  // Effect implicito - dificil saber quando executa
  $: {
    console.log(`Count mudou para: ${count}`);
    localStorage.setItem('count', count);
  }
</script>

Svelte 5 (Runes):

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

  // Effect explicito - claro quando e por que executa
  $effect(() => {
    console.log(`Count mudou para: ${count}`);
    localStorage.setItem('count', count.toString());
  });
</script>

Exemplo Pratico: Todo App com Runes

Vamos construir uma aplicacao de tarefas completa usando Runes:

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

  // Valores derivados
  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)
  );

  // Funcoes de acao
  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 para persistencia
  $effect(() => {
    localStorage.setItem('todos', JSON.stringify(todos));
  });

  // Carregar do localStorage na inicializacao
  $effect(() => {
    const saved = localStorage.getItem('todos');
    if (saved) {
      todos = JSON.parse(saved);
    }
  });
</script>

<div class="todo-app">
  <h1>Minhas Tarefas</h1>

  <form onsubmit={(e) => { e.preventDefault(); addTodo(); }}>
    <input
      type="text"
      bind:value={newTodo}
      placeholder="O que precisa ser feito?"
    />
    <button type="submit">Adicionar</button>
  </form>

  {#if todos.length > 0}
    <div class="controls">
      <button onclick={toggleAll}>
        {allCompleted ? 'Desmarcar' : 'Marcar'} Todas
      </button>

      <div class="filters">
        <button
          class:active={filter === 'all'}
          onclick={() => filter = 'all'}
        >
          Todas
        </button>
        <button
          class:active={filter === 'active'}
          onclick={() => filter = 'active'}
        >
          Ativas
        </button>
        <button
          class:active={filter === 'completed'}
          onclick={() => filter = 'completed'}
        >
          Completas
        </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} tarefa(s) restante(s)</span>
      {#if todos.some(t => t.completed)}
        <button onclick={clearCompleted}>
          Limpar Completas
        </button>
      {/if}
    </footer>
  {/if}
</div>

Por Que Runes Sao Melhores?

1. Reatividade Explicita

Em Svelte 4, qualquer variavel let era automaticamente reativa, o que podia causar confusao:

<!-- Svelte 4: Quando isso e reativo? -->
<script>
  let data = fetch('/api').then(r => r.json()); // Reativo?
  let config = { theme: 'dark' }; // Reativo?
  const MAX = 100; // Nao reativo (const)
</script>

Com Runes, voce declara explicitamente o que e reativo:

<!-- Svelte 5: Clareza total -->
<script>
  let data = $state(null); // Reativo
  let config = $state({ theme: 'dark' }); // Reativo
  const MAX = 100; // Nao reativo (intencional)
</script>

2. Melhor TypeScript Support

Runes funcionam perfeitamente com TypeScript:

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

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

  // Tipos inferidos corretamente
  let completedCount = $derived(
    todos.filter(t => t.completed).length
  );
</script>

3. Reatividade Universal

Runes funcionam fora de componentes .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; }
  };
}

// Em qualquer componente
import { createCounter } from './stores/counter.svelte';

const counter = createCounter(10);

Comparacao com React

Vamos comparar a mesma funcionalidade em React e Svelte 5:

React (Hooks):

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

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

  // useMemo para valores derivados
  const doubled = useMemo(() => count * 2, [count]);

  // useEffect para 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)}>
        Increment by {step}
      </button>
    </div>
  );
}

Svelte 5 (Runes):

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

  // Derivado - sem array de dependencias
  let doubled = $derived(count * 2);

  // Effect - sem array de dependencias
  $effect(() => {
    document.title = `Count: ${count}`;
  });
</script>

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

Diferencas notaveis:

  • Sem arrays de dependencias (menos bugs)
  • Sintaxe mais concisa
  • Binding bidirecional nativo
  • Menos boilerplate

Performance do Svelte 5

O Svelte 5 traz melhorias significativas de performance:

Benchmarks (js-framework-benchmark)

Metrica React 19 Vue 3.5 Svelte 5
Create 1000 rows 45ms 38ms 32ms
Update 1000 rows 38ms 32ms 28ms
Select row 3.2ms 2.8ms 2.1ms
Remove row 35ms 30ms 25ms
Create 10000 rows 450ms 380ms 320ms
Bundle size (gzip) 45KB 35KB 3KB

Destaque: O Svelte compila para JavaScript vanilla, resultando em bundles significativamente menores.

Migrando de Svelte 4 para Svelte 5

Estrategia de Migracao

  1. Atualize o Svelte: npm install svelte@5
  2. Codigo existente funciona: Svelte 5 e retrocompativel
  3. Migre gradualmente: Converta componentes um por vez
  4. Use o modo legacy: <svelte:options runes={false} />

Ferramenta de Migracao

O Svelte oferece uma ferramenta de migracao automatica:

# Migrar arquivos automaticamente
npx sv migrate svelte-5

# Verificar problemas de compatibilidade
npx svelte-check

Padroes de Migracao

Stores para 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 (stores ainda funcionam!)
// Mas Runes sao recomendados para novo codigo
let count = $state(0);
let doubled = $derived(count * 2);

Quando Usar Svelte 5?

Use Svelte 5 quando:

  • Iniciar novo projeto frontend
  • Precisar de performance maxima
  • Quiser menos boilerplate
  • Preferir sintaxe mais proxima de JavaScript vanilla
  • Precisar de bundles menores

Considere outras opcoes quando:

  • Equipe muito experiente em React/Vue
  • Ecossistema de bibliotecas e crucial
  • Precisar de SSR complexo (SvelteKit ainda e mais jovem)

Conclusao

O Svelte 5 com Runes representa uma evolucao significativa na forma como pensamos sobre reatividade em JavaScript. A sintaxe explicita, o melhor suporte a TypeScript e a performance superior fazem dele uma escolha atraente para novos projetos.

Se voce ja trabalha com React ou Vue, os conceitos de Runes serao familiares, mas a implementacao e mais elegante e direta. Vale a pena experimentar em um projeto pessoal para sentir a diferenca.

A comunidade JavaScript continua inovando, e frameworks como Svelte mostram que ainda ha muito espaco para melhorias na forma como construimos aplicacoes web.

Se voce quer explorar mais sobre frameworks JavaScript modernos, recomendo dar uma olhada no artigo sobre React vs Vue em 2025: Qual Framework Escolher onde comparamos as principais opcoes do mercado.

Bora pra cima! 🦅

📚 Quer Aprofundar Seus Conhecimentos em JavaScript?

Este artigo cobriu o Svelte 5 e Runes, mas ha muito mais para explorar no mundo do desenvolvimento moderno.

Desenvolvedores que investem em conhecimento solido e estruturado tendem a ter mais oportunidades no mercado.

Material de Estudo Completo

Se voce quer dominar JavaScript do basico ao avancado, preparei um guia completo:

Opcoes de investimento:

  • 1x de R$9,90 no cartao
  • ou R$9,90 a vista

👉 Conhecer o Guia JavaScript

💡 Material atualizado com as melhores praticas do mercado

Comentários (0)

Esse artigo ainda não possui comentários 😢. Seja o primeiro! 🚀🦅

Adicionar comentário