Skip to content

$state — Reactive State

In React you declare state with useState and always update it through a setter function — you never modify the state variable directly. Svelte 5 flips this completely: $state gives you a plain variable that you mutate directly. The compiler rewrites it behind the scenes into signals, so the UI stays in sync automatically.

React
function Counter() {
const [count, setCount] = React.useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
Svelte
<script>
let count = $state(0);
</script>
<p>Count: {count}</p>
<button onclick={() => count++}>+1</button>
<button onclick={() => count = 0}>Reset</button>

There is no setCount. You write count++ or count = 0 directly — exactly what you would do with a plain JavaScript variable.

React’s useState is shallow — you must spread objects or replace arrays to trigger a re-render. $state is deeply reactive: mutating a nested property or calling array.push() updates the UI automatically.

React
function Profile() {
const [user, setUser] = React.useState({ name: 'Alice', age: 30 });
const [tags, setTags] = React.useState(['svelte', 'react']);
function birthday() {
setUser(u => ({ ...u, age: u.age + 1 }));
}
function addTag() {
setTags(t => [...t, 'js']);
}
return (
<div>
<p>{user.name} is {user.age}</p>
<button onClick={birthday}>Birthday</button>
<p>{tags.join(', ')}</p>
<button onClick={addTag}>Add tag</button>
</div>
);
}
Svelte
<script>
let user = $state({ name: 'Alice', age: 30 });
let tags = $state(['svelte', 'react']);
</script>
<p>{user.name} is {user.age}</p>
<button onclick={() => user.age++}>Birthday</button>
<p>{tags.join(', ')}</p>
<button onclick={() => tags.push('js')}>Add tag</button>

user.age++ and tags.push('js') just work. No spreading, no replacing — Svelte’s proxy-based deep reactivity handles it.

React
function Form() {
const [name, setName] = React.useState('');
const [email, setEmail] = React.useState('');
const [submitted, setSubmitted] = React.useState(false);
function submit() {
setSubmitted(true);
}
if (submitted) return <p>Sent! Hello, {name}.</p>;
return (
<form onSubmit={e => { e.preventDefault(); submit(); }}>
<input value={name} onChange={e => setName(e.target.value)} placeholder="Name" />
<input value={email} onChange={e => setEmail(e.target.value)} placeholder="Email" />
<button type="submit">Send</button>
</form>
);
}
Svelte
<script>
let name = $state('');
let email = $state('');
let submitted = $state(false);
</script>
{#if submitted}
<p>Sent! Hello, {name}.</p>
{:else}
<form onsubmit={e => { e.preventDefault(); submitted = true; }}>
<input bind:value={name} placeholder="Name" />
<input bind:value={email} placeholder="Email" />
<button type="submit">Send</button>
</form>
{/if}
How do you increment a $state counter in Svelte 5?
You have let user = $state({ name: "Alice" }). How do you update user.name to "Bob"?
You call tags.push("new") on a $state array. What happens?