Difference between cookies and localStorage
A localStorage
allows browsers to store data related to the application
in the client. This might seem identical to the purpose of the cookies, but
there are differences, otherwise it could just be called otherCookies
instead of localStorage
.
First, the localStorage is expected to synchronize over multiple tabs and windows, while in cookies, the behavior varies across browsers and is generally not consistent. For inconsistencies I would blame the fact, that cookies are an older technology and were designed with different goal in mind.
Another difference is that cookies, if present, are attached to every user request. For a best user experience, it is preferred to keep the server-client data exchange at minimum. Although we live in a world where the connection speeds are ever-increasing, rural and remote areas tend to follow the suit at a much slower pace. Eliminating the unnecessary data from the round-trip helps a lot. Sending around megabytes with cookies every single request is definitely the way to go when building a slick app.
Cookies, unlike localStorage, have an expiry date. They are also targeted to purge by multiple forms of cleaning or privacy software. In such scenario, user could be happy by removing space on his device whilst simultaneously tackling privacy concerns only to learn that the work he done in your app is not saved anymore. Relying only on the localStorage to save user's work data is not a way to go either, because it might get purged when clearing browser cache.
Lastly, localStorage is not accessible directly on the server, only on the client. Cookies are accessible on the both sides. The API to handle either is thus also different. They are clearly meant for a different tasks.
Here's a simple comparison of the text above:
difference | cookie | localStorage |
---|---|---|
Expiration | yes | no |
Accessibility | server and client | client only |
Availability | on every request | manual access |
Tabs synchronization | varies across browsers | built-in |
Svelte and localStorage
Having the need to have persistent reactive data in the Svelte app I have decided to tackle the problem by starting off with an existing example from REPL.
In order to run the example you have to download it, as it would not run in
the sandboxed environment. I have did so, so you do not need to. The
example was not suitable for my application. It had two undesired features
that I had to fix. First, at the very first load (or after a localStorage
was cleared), the store contained null
value. Secondly, it was not
working with arrays.
For a reference, the example code of the store itself looked like this:
import { writable } from "svelte/store"
const storedTheme = localStorage.getItem("theme")
export const theme = writable(storedTheme)
theme.subscribe(value => {
localStorage.setItem("theme", value === "dark" ? "dark" : "light")
})
First load
It took me a while to understand why the code was getting null even tough there is a default value ingrained in a form of a ternary operator:
value === "dark" ? "dark" : "light"
It became clear to me that method only concerns saving, not retrieving.
When the localStorage is empty, the value retrieved is null
, which is
then passed into to writable store. The store's .subscribe
method is thus
called when we want to .set
the value of the store, which too late in the
lifecycle. The change had to be made when initializing a writable store,
because it's first argument is where the store value initially comes from.
import { writable } from "svelte/store"
const itemName = "storedArray"
const retrieved = localStorage.getItem(itemName)
export const storedArray = writable(parsed === null ? [] : retrieved)
storedArray.subscribe(value => localStorage.setItem(itemName, retrieved))
This approach would work save for the one fact that surprised me when I have discovered for the first time:
localStorage does not support arrays, only strings
That's right - localStorage does not support data structures, only scalars. There are obvious ways around this limitation, once you are aware they exist. Discovering this was the tricky part, because browser did not throw any error trying to save array in the localStorage. Inspecting the localStorage contents via tools the browser provides displayed the values neatly delimited with a comma, resembling and array very closely.
Arrays in localStorage
Fortunately, this problem not a problem anymore. To convert a data
structure to an string, we can serialize it. Common way to do it in JS is
to convert it to JSON using JSON.stringify()
. To revert it back, we use
JSON.parse()
.
import { writable } from "svelte/store"
const itemName = "storedArray"
const retrieved = localStorage.getItem(itemName)
const parsed = JSON.parse(retrieved)
export const storedArray = writable(parsed === null ? [] : parsed)
storedArray.subscribe(value =>
localStorage.setItem(itemName, JSON.stringify(value))
)
Now you can work with persistent store containing an array in your Svelte project as well. As a side note, since objects in JS can be serialized into JSON as well (because JSON is in fact javascript Object Notation, made specifically for this purpose), this approach can be modified to accommodate object in localStorage quite easily.
The code which again, due to sandboxing won't work straight in REPL or can be also cloned from the repository to make it run locally
Father reading
- https://svelte.dev/docs#svelte_store
- https://chasingcode.dev/blog/svelte-persist-state-to-localstorage/
- https://www.tutorialspoint.com/What-is-the-difference-between-local-storage-vs-cookies
- https://stackoverflow.com/questions/3357553/how-do-i-store-an-array-in-localstorage
- https://stackoverflow.com/questions/57089227/inconsistency-when-writing-synchronous-to-localstorage-from-multiple-tabs