Modern web applications are becoming increasingly powerful, blurring the line between websites and native desktop or mobile applications.
A huge part of this power comes from the ability to save data directly in the user’s browser.
Web browser provide us with many tools for persistent data storage like cookies, localStorage, sessionStorage, and IndexedDB.
There are some modern alternatives as well like the Cache API and CookieStore API which are better than legacy options like localStorage as they are asynchronous and can work with service workers.
Many of these options like IndexedDB and Cache API allow a developer to implement storage of things much more complex than simple key-value string pairs. This in turn creates offline functionality, faster load times, and…
Modern web applications are becoming increasingly powerful, blurring the line between websites and native desktop or mobile applications.
A huge part of this power comes from the ability to save data directly in the user’s browser.
Web browser provide us with many tools for persistent data storage like cookies, localStorage, sessionStorage, and IndexedDB.
There are some modern alternatives as well like the Cache API and CookieStore API which are better than legacy options like localStorage as they are asynchronous and can work with service workers.
Many of these options like IndexedDB and Cache API allow a developer to implement storage of things much more complex than simple key-value string pairs. This in turn creates offline functionality, faster load times, and a rich user experience.
Let’s take a look at all these various methods of persistent browser storage, their use cases, advantages, and limitations.
LocalStorage & SessionStorage
localStorage and sessionStorage are part of the Web Storage API and provide simple key-value storage mechanisms.
localStorage: Data stored here has no expiration time. It remains until explicitly deleted by the user or the application.sessionStorage: Data stored here is cleared when the page session ends, i.e., when the browser tab is closed.
Writing and reading data from both localStorage and sessionStorage is straightforward:
// Storing data in localStorage
localStorage.setItem('key', 'value');
// Retrieving data from localStorage
const value = localStorage.getItem('key');
// Storing data in sessionStorage
sessionStorage.setItem('key', 'value');
// Retrieving data from sessionStorage
const sessionValue = sessionStorage.getItem('key');
Both localStorage and sessionStorage are synchronous, which means they can block the main thread, potentially leading to performance issues if used excessively.
Further, because of their synchronous nature, they cannot be used with Service Workers. The data is stored as strings, so complex data structures need to be serialized (e.g., using JSON).
Their storage limits are typically around 5-10MB per origin, which is sufficient for storing these string values.
CookieStore API
The CookieStore API is a modern alternative to traditional cookies, providing an asynchronous way to manage cookies in the browser.
It allows developers to read, write, and delete cookies without blocking the main thread.
Writing its code is mostly similar to traditional cookie operations. Here’s a simple example of how to use the CookieStore API:
// Setting a cookie, inside some async function
await cookieStore.set({ name: 'user_name', value: 'JohnDoe', path: '/', maxAge: 3600 });
// Getting a cookie, inside some async function
const user_name = await cookieStore.get('username');
The CookieStore API is particularly useful for applications that require frequent cookie access without the performance drawbacks of synchronous operations.
Cache API
The Cache API is a powerful tool for storing network requests and responses.
It is primarily used in conjunction with Service Workers to enable offline capabilities and improve load times but can also be used independently.
Unlike Cookies, localStorage, and sessionStorage, the Cache API allows you to store or cache many different types of data (responses) other than just strings, like HTML, CSS, JavaScript files, images, etc.
The storage limit also far exceeds them, though it varies by browser and device. For instance, on Chrome, it can go upto 60% of the available disk space.
Here’s a simple example of how to use the Cache API:
// Open a cache, inside some async function
const cache = await caches.open('my-cache');
// Cache a network request/response
await cache.add('/api/user-data');
// Or cache a custom response for a specific request
const custom_resp = new Response(JSON.stringify({ key: 'value' }), {
headers: { 'Content-Type': 'application/json' }
});
await cache.put('/api/config', custom_resp);
// Retrieve a response from the cache, inside some async function
const response = await cache.match('/api/user-data');
const data = await response.json();
The Cache API is ideal for applications that need to cache resources for offline use or to speed up load times by serving cached content.
IndexedDB
Finally the most powerful and complex option for persistent browser storage is IndexedDB.
You can think of all the above options like a desk drawer of your cabinet. Its right beside you. You open it, put a labeled folder in or take a labeled folder out. Incredibly easy to use.
IndexedDB on the other hand is like a full library, where you can store all types of books, documents, equipment and media. Further, all these items are stored in a structured manner, making it easy to search, retrieve, and manipulate them.
IndexedDB is asynchronous and can hold gigabytes of data. The exact limit depends on the user’s browser and disk space.
Because of all this power, IndexedDB is a bit more complex to use. While it is asynchronous, it is not promise-based but rather event-based, which can make the code verbose and harder to read.
If you have experience with databases, you’ll find IndexedDB familiar as it supports transactions, indexing, and complex queries.
Here’s a an example of how to use IndexedDB:
let db;
// 1. Open a connection to the database. Version 1.
const request = indexedDB.open('myAppDatabase', 1);
// This event runs ONLY when the DB is first created or version is changed
request.onupgradeneeded = function(event) {
db = event.target.result;
// Create an object store (like a table) called "users" with "id" as the primary key
const userStore = db.createObjectStore('users', { keyPath: 'id' });
// Create a name index so we can search by name later
userStore.createIndex('name', 'name', { unique: false });
};
request.onerror = function(event) {
console.error("Database error:", event.target.error);
};
request.onsuccess = function(event) {
db = event.target.result;
console.log("Database opened successfully");
// Now we can perform operations
add_user({ id: 1, name: 'Alice', theme: 'dark' });
};
function add_user(user_object) {
// 2. Create a transaction for read/write access
const transaction = db.transaction(['users'], 'readwrite');
const userStore = transaction.objectStore('users');
// 3. Make a request to add the object
const request = userStore.add(user_object);
request.onsuccess = () => {
console.log("User added successfully!");
};
request.onerror = (err) => {
console.error("Error adding user:", err.target.error);
};
}
This is a simplified "raw" API example. In the real world, you’d almost always use a wrapper library like idb by Jake Archibald to make this much cleaner with Promises.
Conclusion: When to Use What
Here’s a quick summary of when to use each storage method-
CookieStore API: A modern alternative to the traditional cookies. You’d want to use this for small pieces of data that need to be sent to the server with every request automatically, like session identifiers.localStorage: For simple, non-sensitive key-value String pairs that needs to persist across sessions and is less than 5-10MB.sessionStorage: For key-value String pairs that only need to persist for the duration of a page session.Cache API: For caching network requests and responses, especially for offline capabilities and faster load times.IndexedDB: For large amounts of structured data and complex queries. For example, find all users with age > 25 and city = ‘New York’. You can perform such database-like operations withIndexedDB.
Thanks for reading!
p.s. If you liked this article, check out my blog NewGen Stack where I cover more modern web development topics and tools for content creation.