As soon as you see the new $state in Svelte 5, you might be tempted to do the following:
// sharedState.svelte.js export const searchState = $state(""); // This won't work!
<!-- App.svelte --> <script> import { searchState } from './sharedState.svelte.js' function handleClick(){ // This won't work! searchState = "bicycles"; } </script <button onclick={handleClick}>Search for bicycles</button>
This won't work - and here is why:
You're encountering the most complicated part of Svelte 5. How reactivity works and how the compiler hides it from you.
When you export a single value like a number or string, there is no mechanism for Svelte to maintain reactivity because JavaScript doesn't offer a way to track that.
Huge thanks to Mat Simon who explained this to me in simple words ? Here is what I learned:
You can't use a string directly, but all objects (and arrays) in $state get all their values proxied automatically by Svelte 5. Wrapping your string value into an object works:
// sharedState.svelte.js // ❌ export const searchState = $state(""); export const searchState = $state({ text : "" });
This turned into an Svelte state object with a getter and a setter for .text. You can choose any property name you want, as well as multiple properties.
When you import this $state object, and then write .text =, you're using the Svelte setter to update the state:
// App.svelte import { searchState } from './sharedState.svelte.js' function handleClick(){ // uses the automatically created setter searchState.text = "bicycles"; } <button onclick={handleClick}>Search for bicycles</button>
Simple Demo (REPL)): Share $state between components (simple)
And Svelte is really clever about how to proxy these objects. That's why: myList.push('foo') works too for Arrays, because Svelte proxied the push method too.
// data.svelte.js export const myList = $state([]);
When you use an object (including arrays) they are also not states themselves! That's important to understand.
If you do this, you lost reactivity!
import { searchState } from './sharedState.svelte.js'; searchState = {text: "Hello world!"}; // don't do this!
There is no way for Svelte to handle this for you. Always use the automatic Svelte getter/setter via searchState.text = 'new value'.
Okay, objects and arrays are fine and handled by Svelte automatically - we got it.
But what about
Date, URL and more built-in objects of standard JavaScript? And if you're more experienced in JavaScript, you might know that there are some more advanced data types (standard built-in objects):
The Set object lets you store unique values of any type, whether primitive values or object references.
The Map object holds key-value pairs and remembers the original insertion order of the keys.
If you want to use these with reactive $state, you need to use their corresponding Svelte wrapper from svelte/reactivity
// sharedState.svelte.js export const searchState = $state(""); // This won't work!
The reason there is a separate SvelteSet and SvelteMap class (instead of just rewriting it automatically like they do with objects and arrays) is because they wanted to draw a line somewhere since they can't proxy every conceivable object. See https://github.com/sveltejs/svelte/issues/10263 for technical details.
There are multiple options to define your state objects, you can also use classes for custom methods: https://joyofcode.xyz/how-to-share-state-in-svelte-5#using-classes-for-reactive-state
So we know how to import (and update) states inside components and we know that we can use objects and array out of the box with $state:
<!-- App.svelte --> <script> import { searchState } from './sharedState.svelte.js' function handleClick(){ // This won't work! searchState = "bicycles"; } </script <button onclick={handleClick}>Search for bicycles</button>
We can even pass down the $state object as reference by a property with $props:
// sharedState.svelte.js // ❌ export const searchState = $state(""); export const searchState = $state({ text : "" });
// App.svelte import { searchState } from './sharedState.svelte.js' function handleClick(){ // uses the automatically created setter searchState.text = "bicycles"; } <button onclick={handleClick}>Search for bicycles</button>
But how do you know that the state changed somewhere in your app when you're inside a component? That's what $derived and $derived.by are for:
// data.svelte.js export const myList = $state([]);
Simple Demo (REPL)): Share $state between components (simple)
As you might already know, there is no need to write handler functions for text inputs. You can just use bind:value={myStateObj} to update the state automatically:
import { searchState } from './sharedState.svelte.js'; searchState = {text: "Hello world!"}; // don't do this!
Multiple checkbox inputs can be handled with Svelte as well via bind-group={stateObj} - but there is still an open discussion about how to use it correctly with $state.
The good news: There are multiple ways to do this, see below.
One way is to use the onchange event and update the state within the handler function.
Simple Demo (REPL): Search and filter with checkbox group components and v5 $state & $derived
Full SvelteKit example (WIP): https://github.com/mandrasch/austrian-web-dev-companies
I also started Reddit discussion to get more feedback:
What's the easiest way for search & filter checkboxes with Svelte v5 ($state)?
Happy to get your feedback there - or here as comment! ?
Huge thanks to Mat Simon!
The above is the detailed content of Svelte Share state between components (for dummies). For more information, please follow other related articles on the PHP Chinese website!