IndexedDB: What it is, Comparison with LocalStorage, How to use it, and Wrappers

Lorenzo Bloedow
5 min readFeb 28, 2024

--

So, What Is IndexedDB?

Alrighty! Let’s start with the basics.

IndexedDB is a database, indicated by the “DB”, that allows you to store large quantities of structured data on the web.

It’s a SQL-based database with a non-fixed amount of columns that lets you store and retrieve JavaScript objects.

Like most databases, it uses database indexes to allow for ultra-fast access speeds.

Why not LocalStorage?

Storage Limits

First thing we have to consider when talking about storage on the web, is the amount of data you’re able to store.

For LocalStorage the default maximum storage per origin ranges between 5MB and 10MB depending on the browser and version

Whereas for IndexedDB there’s usually either no limit or the limit is so high that it’s pretty much not even worth worrying about it in most cases.

With that we can already see how IndexedDB opens up a world of new possibilities such as storing thousands of images in a local custom cache, something that would usually be impossible with LocalStorage.

Order Of Operations

This one is easy, LocalStorage’s operations are synchronous while IndexedDB’s are asynchronous.

So if, for example, you’re working with small data in a React 18.2 application, it’d usually be a wiser choice to work with LocalStorage since React currently doesn’t support asynchronous components, so you can’t await the operations if needed. Though React 19 will finally have the next-best thing which is the use hook

Browser Support

LocalStorage is a clear winner on this one.

Since it’s one of the first storage APIs to come to browsers, most browsers support LocalStorage since very early versions. Chrome, for example, supports it since version 4!

IndexedDB also has great browser support, but some browsers have only somewhat-recently adopted it. Edge has only had full support since version 79, and Safari since version 10 with a bug that partially breaks it in version 14.1

How To Use It

Some web developers might find IndexedDB off-putting because it’s, well, an entire SQL database that runs on the browser! But despite what they might think, if you don’t need to access the advanced APIs, it’s actually really simple to work with it.

Start by creating a database:

const openRequest = window.indexedDB.open("MediumExampleDatabase");

Then, since IndexedDB operations are asynchronous, we have to listen to its success/failure events:

openRequest.onerror = () => {
console.log("Game Over 💀");
}

// This is called when a database is initialized or its version is updated
openRequest.onupgradeneeded = (event) => {
const db = event.target.result;
}

Now we need to create a table, which is known as an object store in IndexedDB:

const objectStore = db.createObjectStore("users");

And finally, let’s add and retrieve data from our “users” database:

// Add Data
const userId = "9bfe59ff-00a1-4170-8375-bf0c1bf92e94";
const addRequest = objectStore.add({
firstName: "John",
lastName: "Doe",
age: 54,
interests: "Browsing programmingtales.com"
}, userId);

// Get Data
addRequest.onsuccess = () => {
const getRequest = objectStore.get(userId);
getRequest.onsuccess = (event) => {
const result = event.target.result;

console.log(result);
// Output:
// {
// firstName: "John",
// lastName: "Doe",
// age: 54,
// interests: "Browsing programmingtales.com"
// }
}
}

And here’s the full code for our example:

const openRequest = window.indexedDB.open("MediumExampleDatabase");

openRequest.onerror = () => {
console.log("Game Over 💀");
};

openRequest.onupgradeneeded = () => {
const db = event.target.result;
const objectStore = db.createObjectStore("users");

// Add Data
const userId = "9bfe59ff-00a1-4170-8375-bf0c1bf92e94";
const addRequest = objectStore.add({
firstName: "John",
lastName: "Doe",
age: 54,
interests: "Browsing programmingtales.com"
}, userId);

// Get Data
addRequest.onsuccess = () => {
const getRequest = objectStore.get(userId);
getRequest.onsuccess = (event) => {
const result = event.target.result;

console.log(result);
// Output:
// {
// firstName: "John",
// lastName: "Doe",
// age: 54,
// interests: "Browsing programmingtales.com"
// }
}
}
}

IndexedDB Wrappers

If you think working with IndexedDB is a lot of boilerplate or if you hate the event-based architecture, I don’t blame you, it’s actually a very common complaint, so much so that there’s a myriad number of promised-based IndexedDB wrappers on NPM, so let’s look at some of them!

Note: This is a list of IndexedDB wrappers with accompanying example code, I did not write this code and instead took it from the official documentation of the respective wrappers.

Dexie.js


/*
|----------------------------|
| Declare your database |
|----------------------------|
*/

const db = new Dexie('MyDatabase');

// Declare tables, IDs and indexes
db.version(1).stores({
friends: '++id, name, age'
});


/*
|-----------------------|
| Then run some queries |
|-----------------------|
*/

// Find some old friends
const oldFriends = await db.friends
.where('age').above(75)
.toArray();

// or make a new one
await db.friends.add({
name: 'Camilla',
age: 25,
street: 'East 13:th Street',
picture: await getBlob('camilla.png')
});

idb

const db = await openDB(name, version);
const store = db.transaction(storeName).objectStore(storeName);

// Get a value from a store:
const value = await db.get(storeName, key);
// Set a value in a store:
await db.put(storeName, value, key);

jsstore

var dbName ='JsStore_Demo';
var tblProduct = {
name: 'Product',
columns: {
// Here "Id" is name of column
id:{ primaryKey: true, autoIncrement: true },
itemName: { notNull: true, dataType: "string" },
price: { notNull: true, dataType: "number" },
quantity : { notNull: true, dataType: "number" }
}
};
var database = {
name: dbName,
tables: [tblProduct]
}

const isDbCreated = await connection.initDb(database);
if(isDbCreated === true){
console.log("db created");
// here you can prefill database with some data
}
else {
console.log("db opened");
}

var value = {
itemName: 'Blue Jeans',
price: 2000,
quantity: 1000
}

var insertCount = await connection.insert({
into: 'Product',
values: [value]
});

console.log(`${insertCount} rows inserted`);

// results will be array of objects
var results = await connection.select({
from: 'Product',
where: {
id: 5
}
});

alert(results.length + 'record found');

That’s all, folks!

I could talk about IndexedDB all day, there’s so many low-level methods to look at, but this introduction/tutorial was meant as a way to show how to perform simple operations on IndexedDB, so given that, I think it’s best I stop here.

One more thing, if you need a place to run your web server for your awesome website, be sure to check out my post on what are Docker images, which also goes over what Docker is, and also don’t forget to follow me on Twitter!

‘Till next time! 👋

--

--