π Svelte Quick Tip: Connect a store to local storage
Dana Woodman
Posted on March 17, 2021
Local storage, oh my π€©
Here's a really quick tip for you today; how to use Svelte stores to keep data in-sync with local storage.
This is particularly useful if you're wanting to persist some user values, say UI configuration (e.g. their preferred theme, something that is shown/hidden, etc) and have the settings retained for future sessions.
Doing this with Svelte is pretty trivial, let's check it out π
Create the store
All we need to do to connect to local storage is create a writable
store and then set a default value based on local storage and on any change (via subscribe
) we update the local storage entry.
// src/stores/content.js
import { writable } from 'svelte/store'
// Get the value out of storage on load.
const stored = localStorage.content
// or localStorage.getItem('content')
// Set the stored value or a sane default.
export const content = writable(stored || 'Hello, World!')
// Anytime the store changes, update the local storage value.
content.subscribe((value) => localStorage.content = value)
// or localStorage.setItem('content', value)
The key thing to remember here is local storage always stores strings, so if you're storing something else, say a boolean
or some JSON, then you will want to convert to/from the data type you want and the local storage string representation.
For example, if you wanted to store a boolean, it would look more like this:
// src/stores/enabled.ts
import { writable } from 'svelte/store'
export const enabled = writable<boolean>(localStorage.enabled === 'true')
enabled.subscribe((value) => localStorage.enabled = String(value))
Notice that we read the value and compare it to the string 'true'
versus treating it like a boolean
, which won't work. Also note that we need to convert it to a string before saving it to local storage (especially if we're using Typescript).
If you're working with objects or arrays, you can lean towards using JSON.parse
instead:
// src/stores/user.ts
import { writable } from 'svelte/store'
interface User {
email: string
username: string
}
export const enabled = writable<User>(JSON.parse(localStorage.getItem('user')))
enabled.subscribe((value) => localStorage.user = JSON.stringify(value))
Not that we will want to use getItem
instead of the property accessor because getItem
returns null
where as the property accessor returns undefined
on missing keys and null
is valid with JSON.parse
whereas undefined
causes it to explode violently with Uncaught SyntaxError: Unexpected token u in JSON at position 0
.
Use your store
Now you can use the value in your component:
<script>
import { content } from "./store"
</script>
<p>{$content}</p>
<input bind:value={$content} />
Any time you update the value it will be updated in local storage and when you reload it will automatically be set to the value you had set last. Pretty neat!
That's it!
I told you it would be quick π
Hopefully this comes in handy for you, cheers! π»
EDIT: Thanks to Luke Edwards (@lukeed05) on Twitter for pointing out you can do localStorage['content']
(or localStorage.content
) instead of the more verbose localStorage.getItem('content')
and localStorage.content = '...'
instead of localStorage.setItem('content', '...')
EDIT 2: Shoutout to Jamie Birch (@LinguaBrowse) on Twitter who mentioned it might be safer to stick with getItem
and setItem
since they're specifically declared int the local storage spec. It seems safe enough to use the property accessors, but if you want to be extra safe, use getItem
and setItem
.
EDIT 3: SΓΆren (@the_soerenson) on Twitter pointed out you could take this further by adding event listeners so you could detect local storage changes in other browser tabs/windows. Maybe cool if you're trying to sync offline data across browser tabs?
EDIT 4: Thanks to @JuSellier on Twitter who reminded me we can use JSON.parse
on primitive values (boolean
, number
etc), so I've updated the example to use that instead. Thanks JuSellier!
Thanks for reading! Consider giving this post a β€οΈ, π¦ or π to bookmark it for later. π
Have other tips, ideas, feedback or corrections? Let me know in the comments! πββοΈ
Don't forget to follow me on Dev.to (danawoodman), Twitter (@danawoodman) and/or Github (danawoodman)!
Photo by Joshua Aragon on Unsplash
Posted on March 17, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.