IndexedDB (IDB) and LocalStorage(LS) are tools for storing data on the client side. They can save cookies, sessions, and other data. This, however, is where the similarities end. IDB and LS work in different ways and often within different scopes. This article aims to provide a comparison that covers the following points:
- Asynchonsity
- Possible data structures
- Versioning and data migration
- Error handling
- Storage size
- Ease of use
- Cross-platform compatibility
- Security
- Development tools and packages
- Ideal use cases and examples
Asynchronicity
Asynchronous operations are crucial when dealing with data. For example, a chat app needs to retrieve the chat history data of a user, while listening for new messages. These are two different operations. Executing them on a queue would slow things down.
If you used local storage in the above example, you would settle for a queue. But with IndexedDB, you could run data operations concurrently with other functions. This is because IndexedDB can work through web workers. Web workers allow developers to execute scripts in separate threads. Thus, IndexedDB can perform its operations without disrupting the main thread. This is not possible with local storage.
Visualize this process in the image below:
Supported Data Structures
IndexedDB and Local Storage both store data as key-value pairs. This means that related data is identified with a key and saved as a value. For example, a drawer that stores socks would have a key titled ‘socks’. The value would then be all the socks that are stored inside that key.
While IndexedDb and LocalStorage store data in key-value pairs, they don't do it the same way. This is because LocalStorage only accepts strings as the value. IndexedDB, however, allows for strings, arrays, objects, and nested data. IndexedDB also allows you to nest key-value pairs inside parent key-value pairs.
The implication of these differences are two-fold:
- indexedDB can handle complex data, but localStorage can not.
- indexedDB allows for robust querying of the database, while localStorage does not.
Here’s localStorage storing an object, but converting it to a string first. Then retrieve it by converting it from a string to an object.
const user = {
name: 'Jane Smith',
age: 30,
email: 'jane@example.com'
};
localStorage.setItem('user', JSON.stringify(user));
// Retrieving values
const name = localStorage.getItem('name'); // 'John Doe'
const userObj = JSON.parse(localStorage.getItem('user')); // { name: 'Jane Smith', age: 30, email: 'jane@example.com' }
Here is indexedDB storing a string, a nested object, and an array directly.
// Opening an IndexedDB database
const request = window.indexedDB.open('mydatabase', 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
// Creating an object store (similar to a table)
const userStore = db.createObjectStore('users', { keyPath: 'id' });
// Storing a string value
userStore.put({ id: 1, name: 'John Doe' });
// Storing an array
userStore.put({ id: 2, emails: ['john@example.com', 'johnny@example.com'] });
// Storing a nested object
userStore.put({
id: 3,
profile: {
name: 'Jane Smith',
age: 30,
address: {
street: '123 Main St',
city: 'Anytown',
state: 'CA'
}
}
});
};
request.onsuccess = (event) => {
const db = event.target.result;
const transaction = db.transaction(['users'], 'readonly');
const userStore = transaction.objectStore('users');
// Querying the database
const request = userStore.get(3);
request.onsuccess = () => {
console.log(request.result.profile.address.city); // 'Anytown'
};
};
Versioning and Data Migration
Versioning and data migration refer to the evolution of data in a code base. For example, version 1 of a user database could have the following schema:
User: {
Name: 'Paul Graham',
Age: '59'
}
Suppose you add a new feature that requires displaying the occupation of a user You would have to create version 2 as follows:
User: {
Name: 'Paul Graham',
Age: '59',
Occupation: 'Computer Scientist'
}
Something else happens in the above code. The existing data from version 1 has been migrated to version 2. Otherwise, version 2 would not have the same name and age.
The above code snippets are pseudocode, but they accurately represent data versioning and migration. More important though, is that with local storage, these things must be done manually. indexedDB, however, automates the process of data versioning and migration. For this reason, data integrity is higher when using IndexedDB than with localStorage.
Error handling
Both IndexedDB and local storage have their respective mechanisms for error handling.
Since IndexedDB is asynchronous, it uses promises and event listeners for error handling. For every indexedDB transaction, there are built-in resolved/rejected alerts. Here are some examples:
- You request to open a database. IndexedDB’s IDBRequest interface shows you the error or success status of that transaction. IDBRequest works well for several kinds of transactions in IndexedDB
- You have made a request. IndexedDB allows you to handle success with the event handles onsuccess(), and onerror(). This allows you to chain each request with functions that handle specific edge cases.
LocalStorage is synchronous and supports two major methods for error handling
- Try - Catch blocks
- If - else statements
- Return values
Try-catch blocks are used in LocalStorage to log errors like QuotaExceededError. If - else statements also work well with localStorage. For example, while using the .getItem() method to retrieve an item, you could do:
pseudo
if (storage.getItem())
{
return x
}
else {
return y
}
Storage Size and eviction criteria
Another major difference between IndexedDB and LocalStorage is storage size. For localStorage, storage capacity is a fixed 10 MB.
IndexedDB, however, has no fixed capacity. This is because IndexedDB’s capacity depends on the available disk space in the user’s device. As a result, IndexedDB typically has storage space of 1GB, or 60% of the remaining disk space. This is a double-edged sword, as performance will differ for different users.
It is also important to note that IndexedDB and LocalStorage have eviction criteria. Eviction criteria refer to situations in which the data is deleted. Some of these criteria include the following:
Browser-specific policies for storage management impact eviction. For example, Chrome and Firefox use a Least Recently Used (LRU) policy. This means that they delete data when disk space is low.
Criteria set by the developer can also impact eviction. For example, a developer could add an expiration date that triggers the deletion of some data.
User actions can also trigger eviction. A user can delete their data from your site when they clear cookies and other browser data.
These three criteria are consistent for both LocalStorage and IndexedDB.
Ease of use
IndexedDB’s features make it more complex to use than local storage. IndexedDB's learning curve is steep due to its ability to process complex data structures. Code verbosity, error handling, and asynchronous operations are also issues.
LocalStorage has no such issues. The simplicity of its functionality means that you can learn and use it in 20 minutes or less. Here, only strings are used. Asynchonsity is not a factor here either, so your code will follow the main thread. This all contributes to simple, and less verbose code.
Cross-platform compatibility
They are both for the web, so they are not supported for native development. On the web, local storage shines as it has excellent support across all browsers. indexedDb is also well supported, but different browsers have bugs across different browsers. Here’s a forum conversation about some indexedDB bugs in Safari and Firefox.
Security
One of the most important security features for databases on the web is the same origin policy (SOP). It is a safeguard that ensures that a website can only run scripts from its domain. SOP implies that external sources cannot query your database, or run scripts that impact your website. This is useful to prevent cross-site scripting (XSS) attacks. IndexedDB and localStorage both use SOP, but that does not make them invincible. It is prudent for a developer to encrypt any data before storing it in indexedDB or localStorage.
Frameworks and developer support
There is one way around IndexedDB's complexity. That is Dexie.js, a lightweight framework that makes it easier to use IndexedDB. You can also use localForage, a JavaScript library for offline storage. It works as a wrapper for both indexedDB and localStorage. LocalForage was designed to improve the offline experience of web apps.
Conclusion
LocalStorage and IndexedDb are both client-side storage tools. So let’s go over everything we’ve discussed.
- IndexedDb is asynchronous, while LocalStorage is not. This could impact performance
- Data migration and versioning are also built into IndexedDB, unlike localStorage, where things are more manual.
- LocalStorage is easier to use, while IndexedDB is more complex.
- Both tools have the same level of browser support, with local storage being more consistent.
- In terms of security, they both use the Same Origin Policy. However, developers should encrypt any data stored on the client.
- LocalStorage is simple enough that it doesn’t need frameworks. But IndexedDB is easier to use with Dexie JS. LocalForage, however, works to improve the offline experience of web apps and works with both localStorage and IndexedDB.
- Local storage uses try-catch blocks and if-else statements to handle errors. IndexedDB uses event handlers and in-built error handling.
- LoalStorage can only store strings, while IndexedDB can store strings, arrays, objects, and nested objects.
- Local storage has a storage capacity of 10 MB. But IndexedDB could have potentially limitless storage, depending on the free disk space of the user’s device.
With these nine points of comparison, you know enough to choose between IndexedDB and localStorage.
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.