A Svelte store utility library that allows you to extend the writable.
Working with Svelte's get
to retrieve writable values can feel cumbersome, especially with the import clutter from many libraries. withState
, inspired by TanStack Store, allows you to access values directly via store.state
.
This utility library will evolve to include other useful features.
writableWith
?Old way (verbose):
const store = writable('foo');
const value = get(store);
const desiredValue = someRecord[value];
New way (cleaner):
const store = state(writable('foo'));
const desiredValue = someRecord[store.state];
bun add svelte-writable-with
writableWith
is flexible. You can pass a direct value, a writable
, or another writableWith
. This keeps writables predictable and avoids introducing unwanted side effects.
Example:
import { state, previous } from 'svelte-writable-with';
type Modes = "paint" | "pan" | "erase";
const baseMode = writable('paint')
const { set, update, subscribe } = baseMode;
const modeWithState = state(baseMode);
const { set, update, subscribe, state } = baseMode;
const modeWithPreviousAndState = previous(modeWithState)
const { set, update, subscribe, state, previous } = baseMode;
Ensure you provide the primary type in the first "withable" to avoid type issues. (Working on improving this š¤)
ā Correct types:
state(previous(writable<Record<"foo" | "bar", boolean>>({})))
ā Invalid types: previous
will complain
state<Record<"foo" | "bar", boolean>>(previous(writable({})))
Each method can take either a value or a writable.
withState
usage: writableWith.state | withState
Allows access to the store's state directly, without using Svelte's get
state
- Accesses the store state directly, replacing get(store)
.const {
state, // The current state of the writable.
// ... writable return values
} = withState(writable(1337));
Property access:
const currentBenefit = withState(writable<"spinach" | "broccoli">("spinach"));
const vegetableBenefits = {
spinach: "Iron, vitamins, energy",
broccoli: "Fiber, heart health",
};
function getBenefit() {
return vegetableBenefits[currentBenefit.state];
}
withPrevious
usage: writableWith.previous | withPrevious
keeps track of the last value.
[+] property previous
- Returns the previous value before the store was updated.
[%] method subscribe
- previous value as second argument (value, previousValue)
[%] method set
- sets the previous value before setting store state
[%] method update
- sets the previous value before updating store state
const {
subscribe, // Modified subscribe with 2 arguments (`value`, `previousValue`)
set, // Modified set updates `previous` value
update, // Modified update updates `previous` value
previous, // The previous value
} = withPrevious(writable(1337));
setting the writable back to the last value
type States = "paint" | "pan" | "erase";
const mode = withPrevious(writable<States>("paint"));
// Some condition to change mode
mode.set("pan");
// Some condition to return
mode.set(mode.previous);
withLocalStorage
usage: writableWith.localStorage | withLocalStorage
Stores the value in localStorage under a specific key prefixed with svelte-writable-with:
If the initialValue
is a writable
or writableWith
, it initializes the store with the value from localStorage
(if present).
[+] initialises with the localStorage
value for that key or the initialValue
[%] method set
- sets the value in localStorage
- JSON.stringify
-> set
[%] method update
- runs the updater with the value currently in the store and stores the value in localStorage
- JSON.stringify
-> set
withLocalStorage
is still being refined. Here are a few limitations:ā ļø currently keys are not strongly typed and are just strings.
Keys and values are managed through the WithLocalStorageKeys
interface
in your app.d.ts
or global.d.ts
add the following:
declare module 'svelte-writable-with' {
interface WithLocalStorageKeys {
SOME_KEY: number;
}
}
interface
ā
JSON.parse
-ing and JSON.stringify
-ing ā
const {
set, // Modified set updates `localStorage`
update, // Modified update updates `localStorage`
// ... writable return values
} = withLocalStorage('SOME_KEY', writable(1337));
setting the writable back to the last value
type States = "paint" | "pan" | "erase";
const mode = withPrevious(writable<States>("paint"));
// Change the mode
mode.set("pan");
// Revert to the previous mode
mode.set(mode.previous);
withHistory
usage: writableWith.history | withHistory
keeps track of an indefinite history of values.
[+] property history
- Returns the history writable (with state).
[+] method pop
- removes last value from the history object (if it exists), sets the state with the popped value and then returns the popped value
[%] method subscribe
- history value as second argument (value, history)
[%] method set
- pushes the value to history before setting store state
[%] method update
- pushes the value to history before updating store state
const {
subscribe, // Modified subscribe with 2 arguments (`value`, `history`)
set, // Modified set updates `history` value
update, // Modified update updates `history` value
history, // The history writable
pop, // removes last value from history and returns it (updates main store with returned value)
} = withHistory(writable(1337));
setting the writable back to the last value
type States = "paint" | "pan" | "erase";
const mode = withHistory(writable<States>("paint"));
// Some condition to change mode
mode.set("pan");
// history = ['paint']
mode.set("paint");
// history = ['paint', 'pan']
mode.set("erase");
// history = ['paint', 'pan', 'paint']
// Some code...
const paintValue1 = mode.pop();
// paintValue1 === 'paint'
// history === ['paint', 'pan']
const panValue = mode.pop();
// panValue === 'pan'
// history === ['paint']
const paintValue2 = mode.pop();
// paintValue2 === 'paint'
// history === []
const originalMode = get(mode) // "paint"
Using any writable where a $state
rune is expected, i.e. "Type 'WithState<string, Writable<boolean>>' is not assignable to type 'boolean | null | undefined'."
use fromStore
-> import { fromStore } from 'svelte/store';
to create a reactive $state
rune .e.g:
<script lang="ts">
export let isDebuggerEnabled = withLocalStorage(writable(false), 'DebuggerEnabled');
export let isChecked = fromStore(isDebuggerEnabled);
</script>
<input type="checkbox" id="debugger" name="debugger" bind:checked={isChecked.current} />
<label for="debugger">Debugger</label>
The goal of svelte-writable-with
is to offer an intuitive API for extending and enhancing writable
stores based on your specific needs.
history
MIT