Svelte vs Astand Way
Svelte 5 already provides an amazing way to manage global state. Let's imagine that we want to create the classic store for a counter. For example, you could simply place this inside a file called counter.svelte.ts
.
export const counterState = $state({
count: 0,
});
export const increment = () => {
counterState.count += 1;
};
export const decrement = () => {
counterState.count -= 1;
};
// Then, somewhere else
<button onclick={() => increment()}>Increment</button>
<button onclick={() => decrement()}>Decrement</button>
<span>{counterState.count}</span>
You would have something like this:
This approach is amazing and easier than handling subscribe methods on your own with stores.
The goal of Astand in all this is to go a step further and enhance Svelte's reactive state management with additional capabilities.
Now, let's consider the same example using Astand, but this time with TypeScript.
import { createStore, type Store } from 'astand';
interface CounterState {
count: number;
}
export interface CounterStore extends Store<CounterState> {
increment: () => void;
decrement: () => void;
}
const initialState: CounterState = { count: 0 };
const options = {};
const baseCounterStore = createStore<CounterState>(initialState, options);
export const counterStore: CounterStore = {
...baseCounterStore,
increment: () => {
baseCounterStore.setState((prev) => ({ count: prev.count + 1 }));
},
decrement: () => {
baseCounterStore.setState((prev) => ({ count: prev.count - 1 }));
}
};
// Then somewhere else after importing the counterStore
<h1>Counter: {$counterStore.count}</h1>
<button on:click={() => counterStore.increment()}>➕ Increment</button>
<button on:click={() => counterStore.decrement()}>➖ Decrement</button>
We are writing more code to achieve the very same thing. That might not sound very appealing, but did you notice the empty options
object? That's where the magic of Astand comes into play. Here, we can add multiple middlewares, such as debugging or validation, and persist properties, like storing the state after an update in local or session storage. Look how simple it is:
[... same code as before + importing the middlewares]
import { createStore, type Store, consoleLogMiddleware, validationMiddleware } from 'astand';
const options = {
middleware: [
consoleLogMiddleware,
validationMiddleware(
[
{
predicate: (s: { count: number }) => s.count >= 0,
message: 'Count must be non-negative',
level: 'error'
}
])
],
persist: { key: 'counterStore', storage: 'local' }
};
const baseCounterStore = createStore<CounterState>(initialState, options);
export const counterStore: CounterStore = {
...baseCounterStore,
increment: () => {
baseCounterStore.setState((prev) => ({ count: prev.count + 1 }));
},
decrement: () => {
baseCounterStore.setState((prev) => ({ count: prev.count - 1 }));
}
};
// Then somewhere else after importing the counterStore
<h1>Counter: {$counterStore.count}</h1>
<button on:click={() => counterStore.increment()}>➕ Increment</button>
<button on:click={() => counterStore.decrement()}>➖ Decrement</button>
All these features are especially helpful when managing multiple stores with complex states simultaneously.
See also how this state updates through the Debug Panel in the right bottom corner
More middlewares are being added to the Astand ecosystem, so stay tuned!
Go back to Installation