Skip to main content
Props and state are the fundamental ways to manage data in Svelte components. Props pass data from parent to child, while state manages data within a component.

Component Props

Props (short for properties) are how you pass data to components. You pass props just like you pass attributes to elements:
<!-- App.svelte -->
<script>
  import MyComponent from './MyComponent.svelte';
</script>

<MyComponent adjective="cool" count={42} />

Declaring Props with $props

Inside the child component, receive props with the $props rune:
<!-- MyComponent.svelte -->
<script>
  let props = $props();
</script>

<p>This component is {props.adjective}</p>
<p>Count: {props.count}</p>
1
Destructuring Props
2
More commonly, you’ll destructure your props:
3
<script>
  let { adjective, count } = $props();
</script>

<p>This component is {adjective}</p>
<p>Count: {count}</p>
4
Fallback Values
5
Provide default values for props that may not be passed:
6
<script>
  let { adjective = 'happy', count = 0 } = $props();
</script>
7
Renaming Props
8
Use destructuring assignment to rename props:
9
<script>
  let { class: className, super: trouper = 'lights are gonna find me' } = $props();
</script>
10
Rest Props
11
Capture remaining props with a rest property:
12
<script>
  let { title, description, ...others } = $props();
</script>

<div {...others}>
  <h1>{title}</h1>
  <p>{description}</p>
</div>

Type-Safe Props

Add type safety using TypeScript:
<script lang="ts">
  interface Props {
    name: string;
    age: number;
    email?: string;
  }
  
  let { name, age, email = 'N/A' }: Props = $props();
</script>

<div>
  <p>Name: {name}</p>
  <p>Age: {age}</p>
  <p>Email: {email}</p>
</div>
Or using JSDoc:
<script>
  /** @type {{ name: string, age: number }} */
  let { name, age } = $props();
</script>

Component State

The $state rune creates reactive state within a component:
<script>
  let count = $state(0);
</script>

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

Deep Reactive State

Arrays and objects become deeply reactive state proxies:
<script>
  let todos = $state([
    { done: false, text: 'add more todos' }
  ]);
  
  function addTodo() {
    todos.push({ done: false, text: 'new todo' });
  }
  
  function toggleTodo(index) {
    todos[index].done = !todos[index].done;
  }
</script>

<ul>
  {#each todos as todo, i}
    <li>
      <input type="checkbox" bind:checked={todo.done} />
      {todo.text}
    </li>
  {/each}
</ul>

<button onclick={addTodo}>Add Todo</button>

State in Classes

Use $state in class fields:
<script>
  class Todo {
    done = $state(false);
    
    constructor(text) {
      this.text = $state(text);
    }
    
    reset = () => {
      this.text = '';
      this.done = false;
    }
  }
  
  let todo = new Todo('Learn Svelte');
</script>

<div>
  <input type="checkbox" bind:checked={todo.done} />
  <input bind:value={todo.text} />
  <button onclick={todo.reset}>Reset</button>
</div>

Derived State

Create computed values with the $derived rune:
<script>
  let count = $state(0);
  let doubled = $derived(count * 2);
  let quadrupled = $derived(doubled * 2);
</script>

<button onclick={() => count++}>Increment</button>
<p>Count: {count}</p>
<p>Doubled: {doubled}</p>
<p>Quadrupled: {quadrupled}</p>
For complex derivations, use $derived.by:
<script>
  let items = $state([1, 2, 3, 4, 5]);
  
  let stats = $derived.by(() => {
    const sum = items.reduce((a, b) => a + b, 0);
    const average = sum / items.length;
    return { sum, average, count: items.length };
  });
</script>

<p>Sum: {stats.sum}</p>
<p>Average: {stats.average}</p>
<p>Count: {stats.count}</p>

Bindable Props

Create two-way bindings with $bindable:
<!-- FancyInput.svelte -->
<script>
  let { value = $bindable(), ...props } = $props();
</script>

<input bind:value={value} {...props} />

<style>
  input {
    font-family: 'Comic Sans MS';
    color: deeppink;
  }
</style>
<!-- App.svelte -->
<script>
  import FancyInput from './FancyInput.svelte';
  
  let message = $state('hello');
</script>

<FancyInput bind:value={message} />
<p>{message}</p>

Updating Props

References to props update automatically when the prop changes. You can temporarily reassign props:
<!-- Child.svelte -->
<script>
  let { count } = $props();
</script>

<button onclick={() => count += 1}>
  clicks (child): {count}
</button>
However, avoid mutating props unless they are bindable. Use callback props or $bindable for parent-child communication.

Unique IDs with $props.id()

Generate unique IDs for component instances:
<script>
  const uid = $props.id();
</script>

<form>
  <label for="{uid}-firstname">First Name:</label>
  <input id="{uid}-firstname" type="text" />
  
  <label for="{uid}-lastname">Last Name:</label>
  <input id="{uid}-lastname" type="text" />
</form>

Raw State

For non-reactive objects, use $state.raw:
<script>
  let person = $state.raw({
    name: 'Heraclitus',
    age: 49
  });
  
  // Only reassignment works, not mutation
  function birthday() {
    person = { ...person, age: person.age + 1 };
  }
</script>

State Snapshots

Take static snapshots of reactive state:
<script>
  let counter = $state({ count: 0 });
  
  function logSnapshot() {
    console.log($state.snapshot(counter));
  }
</script>

<button onclick={() => counter.count++}>Increment</button>
<button onclick={logSnapshot}>Log Snapshot</button>