πŸš€ Svelte Quick Tip: Connect a store to local storage

danawoodman

Dana Woodman

Posted on March 17, 2021

πŸš€ Svelte Quick Tip: Connect a store to local storage

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)
Enter fullscreen mode Exit fullscreen mode

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))
Enter fullscreen mode Exit fullscreen mode

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))
Enter fullscreen mode Exit fullscreen mode

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} />
Enter fullscreen mode Exit fullscreen mode

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

πŸ’– πŸ’ͺ πŸ™… 🚩
danawoodman
Dana Woodman

Posted on March 17, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related