This article gives all you want to know about different data storage including localStorage
, sessionStorage
, IndexedDB and Cache. After reading this post, you will get to know the differences among them, which storage should you use, how to use them, how much data can I store, etc.
Overview
An overview of the storage types.
- Web Storage
sessionStorage
Temporary session data-
Store key/value pair only for a session, data expires when browser(or tab) is closed.
-
The keys and values are always in the UTF-16 string format.
-
localStorage
Small data -
Store key/value pair persistently.
-
The keys and values are always in the UTF-16 string format.
-
Useful for storing small amounts of data.
-
[IndexedDB](IndexedDB API – Web APIs | MDN) Large data
- A transactional database system,
-
Powerful but complicated for simple cases.
-
Useful for storing large amounts of structured data.
-
Cache Resource caching
Note
Cookies should not be used for storage. They are sent with every HTTP request. If storing data with it will significantly increase the size of a HTTP request.
Deprecated API for storage
- WebSQL
applicationCache
Application cache is being removed from web platform. Consider using service workers instead.
Differences among data storage types
Here are differences among the 4 types of data storage.
Storage | Expire | Asynchronous | Size limit | Security | Web workers |
---|---|---|---|---|---|
sessionStorage | ✓ (Yes) | ✗ (no) | 5MB | Same tab | ✗ (No) |
localStorage | ✗ (No)* |
✗ (No) | Same origin | ✗ (No) | |
IndexedDB | ✗ (No)* |
✓ (Yes) | Large | Same origin | ✓ (Yes) |
Cache | ✗ (No)* |
✓ (Yes) | ✓ (Yes) |
More about expire
If storage limit is reached, data may be cleared out depending on storage type and the browser.
For Safari, data will be deleted after 7 days of use.
webkit: 7-Day Cap on All Script-Writeable Storage
In iOS and iPadOS 13.4 and Safari 13.1 on macOS, it will delete all of a website’s script-writable storage after seven days of Safari use without user interaction on the site. These are the script-writable storage forms affected (excluding some legacy website data types):
- Indexed DB
- LocalStorage
- Media keys
- SessionStorage
- Service Worker registrations and cache
More about security
Access to Web Storage from third-party IFrames is denied if the user has disabled third-party cookies (Firefox implements this behavior from version 43 onwards.)
Note
Cookies use a separate definition of origins compared to that used by Web Storage and IndexedDB. A page can set a cookie for its own domain or any parent domain, as long as the parent domain is not a public suffix.
Storage limit
The global limit for all kinds of storage is dynamic and depends on the browser. According to Storage for the web:
- Firefox: 50% of free disk space.
-
Chrome: 80% of total disk space. An origin can use up to 60%
-
Safari (both desktop and mobile): appears to allow about 1GB.
What happen if the storage limit is reached?
Depending on the browser, data eviction may be triggered or a QuotaExceededError
may be thrown out. The data eviction is based on an LRU policy — the least recently used origin will be deleted first, then the next one, until the browser is no longer over the limit.
See MDN: Browser storage limits and eviction criteria for more information.
Persistent storage
You can request persistent storage for your site to protect critical user or application data. Unless the user removes it, you can protect it from eviction.
// Check if site's storage has been marked as persistent if (navigator.storage && navigator.storage.persist) { const isPersisted = await navigator.storage.persisted(); console.log(`Persisted storage granted: ${isPersisted}`); } // Request persistent storage for site if (navigator.storage && navigator.storage.persist) { const isPersisted = await navigator.storage.persist(); console.log(`Persisted storage granted: ${isPersisted}`); }
How much storage space is taken and available?
In many browsers, you can use [StorageManager API](StorageManager.estimate() – Web APIs | MDN) to asks the Storage Manager for how much storage the current origin takes up (usage
), and how much space is available (quota
).
This method operates asynchronously, so it returns a Promise
which resolves once the information is available. The promise’s fulfillment handler is called with an object containing the usage and quota data.
Example
if (navigator.storage && navigator.storage.estimate) {
navigator.storage.estimate().then(function(estimate) {
console.log('You are currently using about '
+ (estimate.usage / estimate.quota * 100).toFixed(2)
+ '% of your available storage.');
console.log('You can write ' + (estimate.quota - estimate.usage)
+ ' more bytes.');
});
}
Where is the storage data stored?
The actual storage is stored at a SQLite file under directory /
.
MDN: Browser storage limits and eviction criteria:
Where is the data stored?
Each storage type represents a separate repository. Here’s the actual mapping to directories under a user’s Firefox profile (other browsers may differ slightly):
/storage
— the main top-level directory for storages maintained by the quota manager (see below)/storage/permanent
— persistent data storage repository/storage/temporary
— temporary data storage repository/storage/default
— default data storage repository
The profile folder for browsers:
- Firefox
In Firefox, enter
about:support
in the URL bar to open the “Troubleshooting Information” tab. You will find the profile folder is displayed in the “Application Basics” table like below (Firefox v99/Win10):
Profile Folder | C:UsersAppDataRoamingMozillaFirefoxProfileskxx.default-release-xxx |
---|---|
Or you can open the “Troubleshooting Information” page by
Firefox menu > Help > More Troubleshooting Information
.
See more about what information is stored in the profile at Firefox Profile.
An example of a localStorage file (Firefox v99/win10)
/
|- ...
|- cookies.sqlite // ---- Cookies
|- storage/
|- archives/
|- default/ // ---- Kinds of storage for sites
|- https+++xx1.com/
|- cache/
|- idb/
|- ls/ // ---- LocalStorage for xx1.com is saved here
|- data.sqlite // You can open it with a SQLite browser
|- ...
|- permanent/
|- temporary/
In the example above, the LocalStorage data for https://xx1.com` is stored in a SQLite database file --
data.sqlite`. You can open it with a SQLite browser. You will see the same data as you inspect it in the DevTools (in Storage tab) of Firefox.
- Chrome
The profile for chrome:
C:UsersAppDataLocalGoogleChromeUser Data
Storage inspect in DevTools
In DevTools, you can inspect the different storage types and clear data. Chrome v88 provides a feature for you to simulate storage quota. See Storage inspect (The content is also quoted in the end of the article) for more details.
sessionStorage
Opening multiple tabs/windows with the same URL creates sessionStorage
for each tab/window. It means sessionStorage
does not share between multiple tabs/windows.
Availability detecting
See the same section of localStorage
.
Basic usage example
// Save data to sessionStorage
sessionStorage.setItem('key', 'value');
// Get saved data from sessionStorage
let data = sessionStorage.getItem('key');
// Remove saved data from sessionStorage
sessionStorage.removeItem('key');
// Remove all saved data from sessionStorage
sessionStorage.clear();
To write/read some data rather than string, use JSON.stringify()
to convert it to string and use JSON.parse()
to convert it from string.
localStorage
localStorage
is similar to sessionStorage
, except that while localStorage
data has no expiration time.
Availability detecting
Before using localStorage
, it is necessary to detect whether it is available, because it may throw exceptions like Failed to read the loacalStorag in various situations. Below are some of them:
localStorage
is not supported in an early version of a browser.-
The user disables web storage in the browser settings, like the cookie is blocked on Chrome.
-
In a browser’s private browsing mode.
MDN gives a function that detects whether localStorage
or sessionStorage
is both supported and available:
function storageAvailable(type) {
var storage;
try {
storage = window[type];
var x = '__storage_test__';
storage.setItem(x, x);
storage.removeItem(x);
return true;
}
catch(e) {
return e instanceof DOMException && (
// everything except Firefox
e.code === 22 ||
// Firefox
e.code === 1014 ||
// test name field too, because code might not be present
// everything except Firefox
e.name === 'QuotaExceededError' ||
// Firefox
e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
// acknowledge QuotaExceededError only if there's something already stored
(storage && storage.length !== 0);
}
}
And here is how you would use it:
if (storageAvailable('localStorage')) {
// Yippee! We can use localStorage awesomeness
}
else {
// Too bad, no localStorage for us
}
Call storageAvailable('sessionStorage')
for sessionStorage
.
Basic usage example
// Add a localStorage item
localStorage.setItem('myCat', 'Tom');
// Read the localStorage item
const cat = localStorage.getItem('myCat');
// Remove the localStorage item.
localStorage.removeItem('myCat');
To write/read some data rather than string, use JSON.stringify()
to convert it to string and use JSON.parse()
to convert it from string.
IndexedDB
IndexedDB is a way for you to persistently store structured data , including files/blobs, inside a user’s browser. It can work both online and offline.
Usage
IndexedDB is a low-level API which means it is not easy to use. There are some wrappers of IndexedDB to improve usability, below are some:
- localForage: A Polyfill providing a simple name:value syntax for client-side data storage, which uses IndexedDB in the background, but falls back to WebSQL and then localStorage in browsers that don’t support IndexedDB.
-
idb: A tiny (~1.15k) library that mostly mirrors the IndexedDB API, but with small improvements that make a big difference to usability.
-
Dexie.js: A wrapper for IndexedDB that allows much faster code development via nice, simple syntax.
-
idb-keyval: A super-simple-small (~600B) promise-based keyval store implemented with IndexedDB
-
sifrr-storage: A small (~2kB) promise based library for client side key-value storage. Works with IndexedDB, localStorage, WebSQL, Cookies. Can automatically use supported storage available based on priority.
-
JsStore: A simple and advanced IndexedDB wrapper having SQL like syntax.
MDN provides a full IndexedDB example:
Availability detecting
if (window.indexedDB) {
console.log("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.");
}
Cache
Cache is designed to store Request
/ Response
object pairs. It is useful in resource caching.
An origin can have multiple, named Cache objects.
Size limit
Each browser has a hard limit on the amount of cache storage that a given origin can use.
You are responsible for periodically purging cache entries. See Deleting old caches for more information.
Availability detecting
if (window.caches) {
// ...
}
Basic usage example
Use CacheStorage.open()
to open a specific named Cache
object and then call any of the Cache
methods to maintain the Cache
.
You can access CacheStorage
through the global caches
property.
API
// Open a cache
// If the specified Cache does not exist, a new cache is created
// with that cacheName and a Promise that resolves to this new
// Cache object is returned.
caches.open('my-cache').then(cache => {
// Do things with the cache
});
// Add resource to the cache
caches.open('my-cache').then(cache => {
fetch(url).then(function(response) {
if (response.ok) {
cache.put(url, response);
}
});
});
// Obtain the resouce associated with an URL and add it to the cache.
// add(url) takes a URL, retrieves it, and adds the resulting response
// object to the given cache.
caches.open('my-cache').then(cache => {
cache.add(url);
});
// Retrieve a response associated with a request
caches.open('my-cache').then(cache => {
cache.match(url).then(res => {
//...
});
});
// Delete
caches.open('my-cache').then(cache => {
cache.delete(ur);
});
// Delete cache
caches.delete('my-cache').then(() => {
// Deleted
});
Example
A simple example variant from MDN:
async function getCacheFirst(request) {
var cacheAvailable = window.caches ? true : false;
var response = await caches.match(request).catch((err) => {
cacheAvailable = false;
return null;
});
if (response) {
return response;
}
// Not found in the caches or the caches are blocked.
return await fetch(request).then(res => {
if (cacheAvailable) {
var res2 = res.clone();
caches.open('v1').then((cache) => {
cache.put(request, res2);
});
}
return res;
}).catch(err => {
return null;
});
}
In the code above, clone()
is needed because put()
consumes the response body.
Note:
The default example code from MDN has issues if not updated. It fails with
TypeError
orDOMException
.
- If the caches are available (not blocked by the browser), then it fails at
cache.put(event.request, response)
withUncaught TypeError
because the parameterresponse
is null , it has not been saved in the cache yet.If the caches are blocked by the browser (Like the cookies are blocked for the website in the settings of Chrome), it runs the first catch statement and then fails at
caches.open('v1')
in thethen
block withUncaught DOMException
because the cache is not allowed to access.const cachedResponse = caches .match(event.request) .catch(() => fetch(event.request)) .then((r) => { response = r; caches.open("v1").then((cache) => { cache.put(event.request, response); }); return response.clone(); }) .catch(() => caches.match("/gallery/myLittleVader.jpg"));
You can download the whole simple-service-worker source code from simple-service-worker example.
MDN: Cache – Example is another example
Reference
- MDN: Web Storage API
sessionStorage
-
Storage limit is larger than a cookie (at most 5MB).
-
localStorage
-
MDN: Browser storage limits and eviction criteria
> Talks about:
>
> – How much space to allocate to web data storage?
>
> – What to delete when that limit is reached?
>
> – Where is the data stored?
>
> Each storage type represents a separate repository. Here’s the actual mapping to directories under a user’s Firefox profile (other browsers may differ slightly): -
The
CacheStorage
interface represents the storage forCache
objects. -
Set the value of
document.domain
…, and changing the origin in this way does not affect the origin checks used by many Web APIs (e.g.localStorage
,indexedDB
,BroadcastChannel
,SharedWorker
)> ## Cross-origin data storage access
>
> Access to data stored in the browser such as Web Storage and IndexedDB are separated by origin. Each origin gets its own separate storage, and JavaScript in one origin cannot read from or write to the storage belonging to another origin.
>
> Cookies use a separate definition of origins. A page can set a cookie for its own domain or any parent domain, as long as the parent domain is not a public suffix. -
sessionStorage
andlocalStorage
are not accessible from web workers or service workers.> ### Storage inspect
>
> During development, you can use your browser’s DevTools to inspect the different storage types, and easily clear all stored data.
>
> A new feature was added in Chrome 88 that lets you override the site’s storage quota in the Storage Pane. This feature gives you the ability to simulate different devices and test the behavior of your apps in low disk availability scenarios. Go to Application then Storage, enable the Simulate custom storage quota checkbox, and enter any valid number to simulate the storage quota.
>
> While working on this article, I wrote a simple tool to attempt to quickly use as much storage as possible. It’s a quick and easy way to experiment with different storage mechanisms, and see what happens when you use all of your quota.