localStorage
When hosting a repositories' websites on GitHub Pages, all of the websites
from the same GitHub account will share localStorage
among
themselves, because localStorage
is scoped by the origin
(scheme + host name + port):
localStorage
The localStorage
read-only property of the window
interface allows you to access a Storage object for the Document's
origin.
MDN
Web content's origin is defined by the scheme (protocol), hostname (domain), and port of the URL used to access it.MDN
This poses a challenge in case the account hosts multiple independent apps which all use local storage to persist their device-specific state.
localStorage
is keyed by a simple string and most apps will
opt to use short, plain and descriptive names for these keys; the type of
keys that is highly likely to cause a collision when the local storage
instance is reused across multiple sites on the same origin.
It is possible to work around this by meticulously prefixing (or in some other way distinguishing) the local storage keys by hand. However, this places undue burden on the developer and a mistake could lead to data corruption across multiple sites.
I am running into this situation with the many tools I make and store on
GitHub and host via GitHub Pages.
For this reason, I have decided to develop a localStorage
wrapper which automatically prefixes the local storage entries' keys by
the URL path name to make them unique across multiple sites on the same
origin.
github-pages-local-storage
https://$user.github.io/$repoA
localStorage.setItem('test', 'test');
https://$user.github.io/$repoB
localStorage.getItem('test');
// Prints `'test'` not `null`
localStorage.setItem('test', 'test2');
https://$user.github.io/$repoA
localStorage.getItem('test');
// Prints `'test2'` not `test`
As is apparent, the two sites on the same origin but different paths pollute each others local storages unintentionally. This can cause subtle bugs.
github-pages-local-storage
https://$user.github.io/$repoA
import 'https://tomashubelbauer.github.io/github-pages-local-storage/index.js';
localStorage.setItem('test', 'test');
// Stores `'test'` under the key `$repoA:test`
https://$user.github.io/$repoB
import 'https://tomashubelbauer.github.io/github-pages-local-storage/index.js';
localStorage.getItem('test');
// Prints `null`
localStorage.setItem('test', 'test2');
// Stores `'test'` under the key `$repoB:test`
https://$user.github.io/$repoA
import 'https://tomashubelbauer.github.io/github-pages-local-storage/index.js';
localStorage.getItem('test');
// Prints `'test'` as expected
Prefixing the local storage keys has effectively solved the problem of the local storage being shared across the same origin unintentionally.
Add this code at the top of your entry point script:
import 'https://tomashubelbauer.github.io/github-pages-local-storage/index.js';
Use localStorage
as you normally would.
Keys will now be prefixed by location.path
.
You can check this out in action using the form below.
key
, length
, clear
etc.These methods aren't patched / wrapped yet. I generally don't use them all that much. Once I need them, I will patch them so they work with the scope storage. Let me know if you need these.
path
-scoping in SPAs vs MPAsBy using `location.path` as the prefix for the local storage keys, the URL path as a whole becomes the scope of the local storage for that site. That means in case of an MPA (multi-page application) with different URLs for each view, a different local storage instance will be presented on each distinct page/URL. This tool is meant for SPAs (single-page applications).
file:
protocol
When testing the app locally on the file:
protocol with the
github-pages-local-storage
library references, the file's
path becomes the scope of the local storage instance.
This is related to the above section; it is not going to be a problem for SPAs but will for MPAs. It will also be a problem in case the file ever moves to a new path (i.e.: is moved or renamed).
I am not planning on changing the scoping such that it is more friendly to
MPAs because that is not my use-case and it is a non-trivial problem to
solve while still making sense across both live and file:
protocol accesses.