Is there any direct option to persist svelte store data so that even when the page is refreshed, data will be available.
I am not using local storage since I want the values to be reactive.
Is there any direct option to persist svelte store data so that even when the page is refreshed, data will be available.
I am not using local storage since I want the values to be reactive.
You can manually create a subscription to your store and persist the changes to localStorage and also use the potential value in localStorage as default value.
Example
<script>
import { writable } from "svelte/store";
const store = writable(localStorage.getItem("store") || "");
store.subscribe(val => localStorage.setItem("store", val));
</script>
<input bind:value={$store} />
typeof window !== 'undefined'
check before using localStorage. –
Peracid {#if process.browser}
. –
Tezel derived()
, but that will make you have double the amount of stores which is usually unnecessary. –
Tetrapterous $: localStorage.setItem("store", $store);
–
Stressful For Svelte Kit I had issues with SSR. This was my solution based on the Svelte Kit FAQ, the answer by Matyanson and the answer by Adnan Y.
As a bonus this solution also updates the writable if the localStorage
changes (e.g. in a different tab). So this solution works across tabs. See the Window: storage event
Put this into a typescript file e.g. $lib/store.ts
:
import { browser } from '$app/env';
import type { Writable } from 'svelte/store';
import { writable, get } from 'svelte/store'
const storage = <T>(key: string, initValue: T): Writable<T> => {
const store = writable(initValue);
if (!browser) return store;
const storedValueStr = localStorage.getItem(key);
if (storedValueStr != null) store.set(JSON.parse(storedValueStr));
store.subscribe((val) => {
if ([null, undefined].includes(val)) {
localStorage.removeItem(key)
} else {
localStorage.setItem(key, JSON.stringify(val))
}
})
window.addEventListener('storage', () => {
const storedValueStr = localStorage.getItem(key);
if (storedValueStr == null) return;
const localValue: T = JSON.parse(storedValueStr)
if (localValue !== get(store)) store.set(localValue);
});
return store;
}
export default storage
This can be used like this:
import storage from '$lib/store'
interface Auth {
jwt: string
}
export const auth = storage<Auth>("auth", { jwt: "" })
if (storedValueStr == null) return;
needed? Because by the time the storage
event listener runs, this key should already be existing in localStorage. –
Immanuel [null, undefined].includes(val)
strictly equivalent to val == null
? (I see later a loose comparison with null
so just wondering if it could be rewritten for consistency without change in behavior.) –
Fowling f (localValue !== get(store))
? Wouldn't that fail if the value was an (nested) object? –
Cullet Argument of type 'T' is not assignable to parameter of type 'null | undefined'.
in this line if ([null, undefined].includes(val))
–
Viscera key
event property –
Ella From https://github.com/higsch/higsch.me/blob/master/content/post/2019-06-21-svelte-local-storage.md by Matthias Stahl:
Say we have a store variable called count
.
// store.js import { writable } from 'svelte/store'; export const count = writable(0); // App.svelte import { count } from 'store.js';
In order to make the store persistent, just include the function
useLocalStorage
to thestore
object.// store.js import { writable } from 'svelte/store'; const createWritableStore = (key, startValue) => { const { subscribe, set } = writable(startValue); return { subscribe, set, useLocalStorage: () => { const json = localStorage.getItem(key); if (json) { set(JSON.parse(json)); } subscribe(current => { localStorage.setItem(key, JSON.stringify(current)); }); } }; } export const count = createWritableStore('count', 0); // App.svelte import { count } from 'store.js'; count.useLocalStorage();
Then, in your
App.svelte
just invoke theuseLocalStorage
function to enable the persistent state.
This worked perfectly for me in Routify. For Sapper, JHeth suggests "just place count.useLocalStorage()
in onMount
or if (process.browser)
in the component consuming the store. "
count.useLocalStorage()
in onMount
or if (process.browser)
in the component consuming the store. –
Gadolinite Found a library called svelte-persisted-store that implements this functionality. Worked for me.
Install:
npm install svelte-persisted-store
Example from their README:
// in store.ts or similar
import { persisted } from 'svelte-persisted-store'
// First param `preferences` is the local storage key.
// Second param is the initial value.
export const preferences = persisted('preferences', {
theme: 'dark',
pane: '50%',
...
})
// in views
import { get } from 'svelte/store'
import { preferences } from './stores'
preferences.subscribe(...) // subscribe to changes
preferences.update(...) // update value
preferences.set(...) // set value
get(preferences) // read value
$preferences // read value with automatic subscription
In case someone needs to get this working with JavaScript objects:
export const stored_object = writable(
localStorage.stored_object? JSON.parse(localStorage.stored_object) : {});
stored_object.subscribe(val => localStorage.setItem("stored_object",JSON.stringify(val)));
The benefit is that you can access the writable object with the $ shorthand, e.g.
<input type="text" bind:value={$stored_object.name}>
<input type="text" bind:value={$stored_object.price}>
TLDR: Here is a function that takes care of not only setting and getting, but also deletion.
function persistent(name) {
const value = writable(localStorage.getItem(name));
value.subscribe(val => [null, undefined].includes(val) ? localStorage.removeItem(name) : localStorage.setItem(name, val));
return value;
}
export const my_token = persistent('token');
Reasoning: Contrary to intuition, localStorage.setItem('someval', null)
would not set return null for the next localStorage.getItem('someval')
but "null"
which is likely not what one would want. Thus, this also checks for undefined and null and deletes the item accordingly.
my_token.set("hello")
but it not clear on how to use that function to get
the value from the my_token.js store function. I can see the value "hello" in the browser dev tools --> Applications --> Local Storage screen, but your words are Here is a function that takes care of not only setting and getting, but also deletion. I'm just not understanding how the get()
works here.. Note: my_token.set(null);
works great to delete the value in LocalStorage. Where is .get()
–
Flamenco import { get } from "svelte/store";
Would you be offended if I proposed an edit to your code that showed it in use ? –
Flamenco You may want to also check this one out https://github.com/andsala/svelte-persistent-store
Also, if you use sapper and don't want something to run on the server, you can use the onMount hook
onMount(() => {
console.log('I only run in the browser');
});
This function synchronises svelte store with localStorage. If there is no value stored it takes the initValue parameter instead.
I also added Typescript.
import { writable, Writable } from 'svelte/store';
const wStorage = <T>(key: string, initValue: T): Writable<T> => {
const storedValueStr = localStorage.getItem(key);
const storedValue: T = JSON.parse(storedValueStr);
const store = writable(storedValueStr != null ? storedValue : initValue);
store.subscribe((val) => {
localStorage.setItem(key, JSON.stringify(val));
})
return store;
}
export default wStorage;
You can then use the function elsewhere like you are used to with writable
:
const count = wStorage<number>('count', 0);
Edit: If you are using SSR in your app and don't want to use onMount
or check if (process.browser)
for every writable method. Here is a modified version:
const wStorage = <T>(key: string, initValue: T): Writable<T> => {
const store = writable(initValue);
if (typeof Storage === 'undefined') return store;
const storedValueStr = localStorage.getItem(key);
if (storedValueStr != null) store.set(JSON.parse(storedValueStr));
store.subscribe((val) => {
localStorage.setItem(key, JSON.stringify(val));
})
return store;
}
wStorage
function. How many times you call it, that many times is the subscription initialized. I use the wStorage
in src/store.ts
file, just how it is in the docs. I believe the code runs there only once, am I missing something? If you call the wStorage
function in component, feel free to modify it (e.g. returning [store, unsubscribe]
and then using onDestroy(unsubscribe);
in the component). –
Danford With svelte 3.38 and svelte-kit (Sapper's succesor) , I use:
<script>
import { onMount } from 'svelte';
import { writable } from "svelte/store";
let value;
onMount(() => {
value = writable(localStorage.getItem("storedValue") || "defaut value");
value.subscribe(val => localStorage.setItem("storedValue", val));
})
</script>
<input bind:value={$value} />
localStorage
isn't available out of onMount()
localStorage
is available outside onMount()
–
Estheresthesia You may do it like this:
import { writable } from 'svelte/store';
import { browser } from '$app/environment';
// check if the item exists in local storage, if so, return the item, otherwise, return null. (This is to avoid errors on initial reads of the store)
// browser && makes sure the command only works in the client side (browser).
const get_local_storage =
browser && localStorage.getItem('presisted_local_store')
? browser && localStorage.getItem('presisted_local_store')
: null;
// create a writable store
export const presisted_local_store = writable(JSON.parse(get_local_storage));
// create a subscribe method for the store to write back to the local storage (again, on the browser)
presisted_local_store.subscribe((value) => {
browser && localStorage.setItem('presisted_local_store', JSON.stringify(value));
Works for me with svelte version 3.44.1
.
src/store.js file:
import { writable } from "svelte/store";
import { browser } from "$app/env"
export const fontSize = writable(browser && localStorage.getItem("fontSize") || "15");
fontSize.subscribe((value) => {
if (browser) return localStorage.setItem("fontSize", value)
});
copied this code from one of my projects
$lib/savable.ts
import type { Writable, StartStopNotifier, Unsubscriber } from 'svelte/types/runtime/store';
import { writable } from 'svelte/store';
const attach = (writable: Writable<unknown>, key='store'): void =>{
const json = localStorage.getItem(key);
if (json) {
writable.set(JSON.parse(json));
}
writable.subscribe(current => {
localStorage.setItem(key, JSON.stringify(current));
});
}
interface Savable<T> extends Writable<T> {
mount(localstore: Storage): void
dismount(localstore: Storage): JSON
unsub: Unsubscriber
}
function savable<T>(key: string, value?: T, start?: StartStopNotifier<T>): Savable<T>{
const base = writable(value, start)
return {
...base,
mount(localstore) {
if(this.mounted) throw new Error("Already mounted");
this.mounted = true;
const json = localstore.getItem(key);
if (json) {
base.set(JSON.parse(json));
}
this.unsub = base.subscribe(current => {
localStorage.setItem(key, JSON.stringify(current));
});
console.log(this)
},
dismount(localstore) {
if(!this.mounted) throw new Error("Not mounted");
const json = JSON.parse(localstore.getItem(key))
this.unsub()
localstore.removeItem(key)
return json
},
unsub() {
throw new Error('Cannot unsubscribe when not subscribed')
}
}
}
export {
attach,
savable,
};
export type {
Savable
}
export default savable
here is an example of a savable being used in index.svelte
<!—- Typescript is not required —->
<script lang=ts>
import savable from `$lib/savable`;
const value = savable(‘input_value’);
import { onMount } from ‘svelte’;
onMount(()=>{
value.mount()
})
</script>
<input bind:value={$value}></input>
In SvelteKit the official method is called Snapshot:
© 2022 - 2024 — McMap. All rights reserved.