Getting started with Svelte - St
Not all application state belongs inside your application's component hierarchy. Sometimes, you'll have values that need to be accessed by multiple unrelated components, or by a regular JavaScript module.
In Svelte, we do this with stores. A store is simply an object with a subscribe
method that allows interested parties to be notified whenever the store value changes.
Writable stores
Function that creates a store which has values that can be set from 'outside' components. It gets created as an object with additional set
and update
methods.
set
is a method that takes one argument which is the value to be set. The store value gets set to the value of the argument if the store value is not already equal to it.
update
is a method that takes one argument which is a callback. The callback takes the existing store value as its argument and returns the new value to be set to the store.
import { writable } from 'svelte/store';
export const count = writable(0);
count.subscribe((value) => {
console.log(value);
}); // logs '0'
count.set(1); // logs '1'
count.update((n) => n + 1); // logs '2'
Auto subscriptions
If the store is subscribed to, but never unsubscribed and the component was instantiated and destroyed many times, this would result in a memory leak.
You can reference a store value by prefixing the store name with $
, this will auto subscribe and unsubscribe the store.
You're not limited to using $
started store inside the markup, either — you can use it anywhere in the <script>
as well, such as in event handlers or reactive declarations.
Any name beginning with $
is assumed to refer to a store value. It's effectively a reserved character — Svelte will prevent you from declaring your own variables with a $
prefix.
Note that auto subscription only works with store variables that are declared (or imported) at the top-level scope of a component.
Readable stores
Not all stores should be writable by whoever has a reference to them. For example, you might have a store representing the mouse position or the user's geolocation, and it doesn't make sense to be able to set those values from 'outside'. For those cases, we have readable stores.
The first argument to readable
is an initial value, which can be null
or undefined
if you don't have one yet. The second argument is a start
function that takes a set
callback and returns a stop
function. The start
function is called when the store gets its first subscriber; stop
is called when the last subscriber unsubscribes.
stores.js
import { readable } from 'svelte/store';
export const time = readable(new Date(), function start(set) {
const interval = setInterval(() => {
set(new Date());
}, 1000);
return function stop() {
clearInterval(interval);
};
});
App.svelte
<script>
import { time } from './stores.js';
const formatter = new Intl.DateTimeFormat(
'en',
{
hour12: true,
hour: 'numeric',
minute: '2-digit',
second: '2-digit'
}
);
</script>
<h1>The time is {formatter.format($time)}</h1>
Derived stores
You can create a store whose value is based on the value of one or more other stores with derived
.
It's possible to derive a store from multiple input stores, and to explicitly set a value instead of returning it (which is useful for deriving values asynchronously).
stores.js
import { readable, derived } from 'svelte/store';
export const time = readable(new Date(), function start(set) {
const interval = setInterval(() => {
set(new Date());
}, 1000);
return function stop() {
clearInterval(interval);
};
});
const start = new Date();
export const elapsed = derived(
time,
($time) => Math.round(($time - start) / 1000)
);
App.svelte
<script>
import { time, elapsed } from './stores.js';
const formatter = new Intl.DateTimeFormat(
'en',
{
hour12: true,
hour: 'numeric',
minute: '2-digit',
second: '2-digit'
}
);
</script>
<h1>The time is {formatter.format($time)}</h1>
<p>
This page has been open for
{$elapsed}
{$elapsed === 1 ? 'second' : 'seconds'}
</p>
Custom stores
As long as an object correctly implements the subscribe
method, it's a store. Beyond that, anything goes. It's very easy, therefore, to create custom stores with domain-specific logic.
stores.js
import { writable } from 'svelte/store';
function createCount() {
const { subscribe, set, update } = writable(0);
return {
subscribe,
increment: () => update((n) => n + 1),
decrement: () => update((n) => n - 1),
reset: () => set(0)
};
}
export const count = createCount();
App.svelte
<script>
import { count } from './stores.js';
</script>
<h1>The count is {$count}</h1>
<button on:click={count.increment}>+</button>
<button on:click={count.decrement}>-</button>
<button on:click={count.reset}>reset</button>
Store bindings
If a store is writable — i.e. it has a set
method — you can bind to its value, just as you can bind to local component state.
stores.js
import { writable, derived } from 'svelte/store';
export const name = writable('world');
export const greeting = derived(name, ($name) => `Hello ${$name}!`);
App.svelte
<script>
import { name, greeting } from './stores.js';
</script>
<h1>{$greeting}</h1>
<input bind:value={$name} />
<button on:click={() => $name += '!'}>
Add exclamation mark!
</button>
Changing the input value will now update name
and all its dependents.
The $name += '!'
assignment is equivalent to name.set($name + '!')
.