Svelte logo

Svelte by Example: State

Svelte uses the $state rune to hold local component state. When state changes, Svelte updates the interface.

In this example, we'll create an AddTodo component to add new todos, and bind our checkboxes to the list.

Wrap an initial value in $state to make it reactive. Read and write it like a regular variable.

Call the onadd callback with the new todo, then clear the input.

Use bind:value to keep the input and task in sync.

AddTodo.svelte
<script>
  let { onadd } = $props();

  let task = $state('');



  function submit(event) {
    event.preventDefault();
    onadd({ task, completed: false });
    task = '';
  }
</script>

<form onsubmit={submit}>
  <label>
    Task


    <input type="text" bind:value={task} required>
  </label>
  <button>Add</button>
</form>

Mark a prop with $bindable to let a parent bind to it. The default value is false.

bind:checked keeps the checkbox and completed in sync.

Todo.svelte
<script>
  let {
    task,
    completed = $bindable(false),
  } = $props();
</script>



<label>
  <input type="checkbox" bind:checked={completed}>
  {task}
</label>

Arrays and plain objects in $state are deeply reactive. We can push a todo without reassigning the array.

Bind the child's completed prop to the todo. Checking a box now updates the parent's state too.

Pass addTodo as a callback prop, just like in the events example.

TodoList.svelte
<script>
  import Todo from './Todo.svelte';
  import AddTodo from './AddTodo.svelte';

  let todos = $state([]);

  function addTodo(todo) {
    todos.push(todo);
  }
</script>

{#each todos as todo (todo)}
  <Todo
    task={todo.task}


    bind:completed={todo.completed}
  />
{/each}

<AddTodo onadd={addTodo} />

Deep State

Updating a property also triggers an update. This applies to the plain objects inside our $state array.

TodoList.svelte
<script>
  let todos = $state([
    { task: 'Mow lawn', completed: false },
  ]);

  function completeAll() {
    for (const todo of todos) {
      todo.completed = true;
    }
  }
</script>

<button onclick={completeAll}>Complete all</button>

{#each todos as todo (todo)}
  <p>{todo.task}: {todo.completed ? 'Done' : 'To do'}</p>
{/each}