Reputation: 1698
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.
Upvotes: 66
Views: 31926
Reputation: 890
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
Upvotes: 5
Reputation: 107
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));
Upvotes: 1
Reputation: 51
In SvelteKit the official method is called Snapshot:
https://kit.svelte.dev/docs/snapshots
Upvotes: 0
Reputation: 75
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>
Upvotes: 0
Reputation: 886
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)
});
Upvotes: 0
Reputation: 7239
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: "" })
Upvotes: 18
Reputation: 321
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;
}
Upvotes: 2
Reputation: 55
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()
Upvotes: 2
Reputation: 3250
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.
Upvotes: 4
Reputation: 346
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}>
Upvotes: 4
Reputation: 1266
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. "
Upvotes: 15
Reputation: 161
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');
});
Upvotes: 2
Reputation: 112917
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} />
Upvotes: 88