Skip to main content
The $state rune allows you to create reactive state, which means that your UI reacts when it changes.
<script>
	let count = $state(0);
</script>

<button onclick={() => count++}>
	clicks: {count}
</button>
Unlike other frameworks you may have encountered, there is no API for interacting with state — count is just a number, rather than an object or a function, and you can update it like you would update any other variable.

Signature

function $state<T>(initial: T): T;
function $state<T>(): T | undefined;
initial
T
The initial value for the state. If not provided, the state will be undefined.

Deep state

If $state is used with an array or a simple object, the result is a deeply reactive state proxy. Proxies allow Svelte to run code when you read or write properties, including via methods like array.push(...), triggering granular updates. State is proxified recursively until Svelte finds something other than an array or simple object (like a class or an object created with Object.create):
let todos = $state([
	{
		done: false,
		text: 'add more todos'
	}
]);
Modifying an individual todo’s property will trigger updates to anything in your UI that depends on that specific property:
todos[0].done = !todos[0].done;
If you push a new object to the array, it will also be proxified:
todos.push({
	done: false,
	text: 'eat lunch'
});
When you update properties of proxies, the original object is not mutated.
Note that if you destructure a reactive value, the references are not reactive — as in normal JavaScript, they are evaluated at the point of destructuring.
let { done, text } = todos[0];

// this will not affect the value of `done`
todos[0].done = !todos[0].done;

Classes

Class instances are not proxied. Instead, you can use $state in class fields (whether public or private), or as the first assignment to a property immediately inside the constructor:
class Todo {
	done = $state(false);

	constructor(text) {
		this.text = $state(text);
	}

	reset() {
		this.text = '';
		this.done = false;
	}
}
The compiler transforms done and text into get/set methods on the class prototype referencing private fields. This means the properties are not enumerable.

$state.raw

function raw<T>(initial: T): T;
function raw<T>(): T | undefined;
In cases where you don’t want objects and arrays to be deeply reactive you can use $state.raw. State declared with $state.raw cannot be mutated; it can only be reassigned:
let person = $state.raw({
	name: 'Heraclitus',
	age: 49
});

// this will have no effect
person.age += 1;

// this will work, because we're creating a new person
person = {
	name: 'Heraclitus',
	age: 50
};
This can improve performance with large arrays and objects that you weren’t planning to mutate anyway, since it avoids the cost of making them reactive. Note that raw state can contain reactive state (for example, a raw array of reactive objects).

$state.snapshot

function snapshot<T>(state: T): Snapshot<T>;
To take a static snapshot of a deeply reactive $state proxy, use $state.snapshot:
<script>
	let counter = $state({ count: 0 });

	function onclick() {
		// Will log `{ count: ... }` rather than `Proxy { ... }`
		console.log($state.snapshot(counter));
	}
</script>
This is handy when you want to pass some state to an external library or API that doesn’t expect a proxy, such as structuredClone.

$state.eager

function eager<T>(value: T): T;
When state changes, it may not be reflected in the UI immediately if it is used by an await expression, because updates are synchronized. In some cases, you may want to update the UI as soon as the state changes. For example, you might want to update a navigation bar when the user clicks on a link:
<nav>
	<a href="/" aria-current={$state.eager(pathname) === '/' ? 'page' : null}>home</a>
	<a href="/about" aria-current={$state.eager(pathname) === '/about' ? 'page' : null}>about</a>
</nav>
Use this feature sparingly, and only to provide feedback in response to user action — in general, allowing Svelte to coordinate updates will provide a better user experience.

Sharing state across modules

You can declare state in .svelte.js and .svelte.ts files, but you can only export that state if it’s not directly reassigned. You have two options: Option 1: Don’t reassign it
// This is allowed — since we're updating
// `counter.count` rather than `counter`,
// Svelte doesn't wrap it in `$.state`
export const counter = $state({
	count: 0
});

export function increment() {
	counter.count += 1;
}
Option 2: Don’t directly export it
let count = $state(0);

export function getCount() {
	return count;
}

export function increment() {
	count += 1;
}