DEV Community

Cover image for 9 differences between IndexedDB and LocalStorage
Armstrong Olusoji
Armstrong Olusoji

Posted on

9 differences between IndexedDB and LocalStorage

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:

  1. Asynchonsity
  2. Possible data structures
  3. Versioning and data migration
  4. Error handling
  5. Storage size
  6. Ease of use
  7. Cross-platform compatibility
  8. Security
  9. Development tools and packages
  10. 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:

A flow chart demonstrating multi- and single-threaded operations with IndexedDB and LocalStorage

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:

  1. indexedDB can handle complex data, but localStorage can not.
  2. 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' }



Enter fullscreen mode Exit fullscreen mode

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'
  };
};


Enter fullscreen mode Exit fullscreen mode

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'
}


Enter fullscreen mode Exit fullscreen mode

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'
}


Enter fullscreen mode Exit fullscreen mode

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:

  1. 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
  2. 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

  1. Try - Catch blocks
  2. If - else statements
  3. 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

}

Enter fullscreen mode Exit fullscreen mode




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:

  1. 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.

  2. 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.

  3. 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.

  1. IndexedDb is asynchronous, while LocalStorage is not. This could impact performance
  2. Data migration and versioning are also built into IndexedDB, unlike localStorage, where things are more manual.
  3. LocalStorage is easier to use, while IndexedDB is more complex.
  4. Both tools have the same level of browser support, with local storage being more consistent.
  5. In terms of security, they both use the Same Origin Policy. However, developers should encrypt any data stored on the client.
  6. 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.
  7. Local storage uses try-catch blocks and if-else statements to handle errors. IndexedDB uses event handlers and in-built error handling.
  8. LoalStorage can only store strings, while IndexedDB can store strings, arrays, objects, and nested objects.
  9. 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 (2)

Collapse
 
sb-jeremias profile image
Jeremias

Great breakdown! One additional point worth highlighting is how IndexedDB handles binary data. While the article mentions IndexedDB can store arrays and objects, it's also capable of storing binary data directly, like Uint8Array or ArrayBuffer, without any need for serialization.

This is a big deal if you're working with image files, PDFs, or other binary-heavy data. With localStorage, you'd have to convert binary data to Base64 (which increases its size by ~33%) before storing it, and then convert it back when you retrieve it — not ideal for performance. IndexedDB, however, lets you store and retrieve binary data as-is.

We ran into this exact challenge while working on the Scanbot Web SDK, which processes large image files in-browser. Our solution involved combining IndexedDB with web workers to handle large image files off the main thread, ensuring a smooth user experience. This approach avoids UI freezes and boosts performance significantly.

If you're interested in the details of that approach, we shared it in this blog post: scanbot.io/techblog/storage-wars-w.... I think the approach could be useful to others dealing with large binary files in IndexedDB.

Collapse
 
munashe_njanji profile image
The Stoic

Hi nice article, can you please send the flowchat please in a better resolution

Some comments may only be visible to logged-in visitors. Sign in to view all comments.