State: $state
State is component data that changes while the page is open: text in a field, a list of tasks, a counter. For Svelte to update the page when it changes, the variable is declared with the $state rune.
A plain variable does not update the page
<!-- src/routes/+page.svelte -->
<script>
let count = 0;
</script>
<button onclick={() => count++}>Done: {count}</button>The onclick attribute sets a function that runs on click (more in the lesson on events). The number in the variable grows, but the button always shows "Done: 0": Svelte does not track plain variables.
The $state rune
<!-- src/routes/+page.svelte -->
<script>
let count = $state(0);
</script>
<button onclick={() => count++}>Done: {count}</button>Now each click increases the number on the button. $state(0) creates a reactive variable with the initial value 0. You read and change it like a regular variable — no .value and no setter functions.
Arrays and objects
State can hold an array of objects — for example, a list of tasks. Svelte notices changes inside it: push, assigning a property, splice.
<!-- src/routes/+page.svelte -->
<script>
let todos = $state([
{ id: 1, text: 'Buy bread', done: false },
{ id: 2, text: 'Call Oleg', done: false }
]);
function addTodo() {
todos.push({ id: Date.now(), text: 'New task', done: false });
}
function finishFirst() {
todos[0].done = true;
}
</script>
<p>Tasks: {todos.length}</p>
<p>First one done: {todos[0].done ? 'yes' : 'no'}</p>
<button onclick={addTodo}>Add</button>
<button onclick={finishFirst}>Finish the first</button>The "Add" button increases the task counter, the "Finish the first" button changes "no" to "yes". Svelte gives this deep reactivity to plain arrays and objects passed to $state.
Replacing the whole value
A state variable can be assigned a new value — that updates the page too:
<script>
let todos = $state([{ id: 1, text: 'Buy bread', done: true }]);
function clearDone() {
todos = todos.filter((todo) => !todo.done);
}
</script>
<p>Tasks: {todos.length}</p>
<button onclick={clearDone}>Remove finished</button>After the click the finished task disappears and the counter shows "Tasks: 0".
Where $state is allowed
The rune works only when declaring a variable or a class field — in a .svelte component or in a .svelte.js file (covered in the chapter on shared state). In a plain .js file it does not work: the Svelte compiler does not process such files.
Common mistake
Declaring state with const and then assigning a new value:
<script>
const todos = $state([]);
function reset() {
todos = []; // Error: cannot assign to a constant
}
</script>If the variable has to be replaced as a whole, declare it with let. const is fine when you only change the contents — call push or assign properties.