01 - State ($state)

What is $state?

$state() creates reactive state — when the value changes, the UI automatically updates. It replaces Svelte 4's implicit reactivity where all top-level let variables were reactive.

Svelte 4 (Legacy)

<script>
let count = 0;  // all top-level let variables were automatically reactive
</script>

Svelte 5

<script>
let count = $state(0);  // explicitly opt-in to reactivity
</script>

The difference: in Svelte 5, only variables wrapped in $state() are reactive. A plain let is just a regular JavaScript variable.

Basic Usage

<!-- src/lib/components/Counter.svelte -->
<script>
let count = $state(0);
</script>

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

Objects and Arrays

$state() has deep reactivity by default — mutating nested properties or array methods automatically triggers UI updates.

<!-- src/lib/components/UserProfile.svelte -->
<script>
let user = $state({ name: 'Alice', age: 25 });
let items = $state([1, 2, 3]);
</script>

<button onclick={() => user.age++}>Birthday</button>  <!-- deep mutation tracked -->
<button onclick={() => items.push(items.length + 1)}>Add Item</button>  <!-- array mutation tracked -->

In Svelte 4, you had to reassign arrays/objects to trigger updates (items = [...items, newItem]). In Svelte 5, items.push() just works.

Key Points

  • Replaces Svelte 4's implicit reactivity — now you explicitly opt-in with $state()
  • Works with primitives, objects, and arrays
  • Deep reactivity by default — nested mutations are tracked automatically
  • Plain let without $state() is not reactive

Interactive Example

<script>
let count = $state(0);
let user = $state({ name: 'Alice', age: 25 });
let items = $state([1, 2, 3]);
</script>

<div>
  <h2>01 - State Example</h2>
  
  <div>
    <h3>Counter</h3>
    <button onclick={() => count++}>Clicks: {count}</button>
    <button onclick={() => count = 0}>Reset</button>
  </div>
  
  <div>
    <h3>Object State</h3>
    <p>Name: {user.name}, Age: {user.age}</p>
    <button onclick={() => user.age++}>Birthday</button>
    <input bind:value={user.name} placeholder="Change name" />
  </div>
  
  <div>
    <h3>Array State</h3>
    <p>Items: {items.join(', ')}</p>
    <button onclick={() => items.push(items.length + 1)}>Add Item</button>
    <button onclick={() => items.pop()}>Remove Last</button>
  </div>
</div>

<style>
div { margin: 20px; }
h3 { margin-top: 20px; }
button { margin: 5px; }
</style>