IndexedDB is a low-level API for storing large amounts of structured data in the browser—including files and blobs. Unlike localStorage and sessionStorage, which only hold strings, IndexedDB gives you object stores, indexes, transactions, and key-based access. That makes it the right choice for offline apps, cached API responses, and complex client-side data.
This page is your central hub: learn core concepts here, browse 4 dedicated CRUD tutorials below, and practice with live Try It examples. If you are coming from Web SQL, think of object stores as tables—but you work with JavaScript objects, not SQL strings.
What You’ll Learn
01
open()
Create or open a DB.
02
Object stores
Save JS objects.
03
Transactions
Atomic read/write.
04
Cursors
Walk every record.
05
CRUD hub
4 deep tutorials.
06
Try It
Live browser demos.
Fundamentals
What Is IndexedDB?
IndexedDB is a transactional database system built into modern browsers. You store and retrieve JavaScript objects using keys—similar to a NoSQL document store. The API is entirely asynchronous: requests return immediately and fire callbacks or resolve promises later, so the main thread never blocks while disk I/O runs.
IndexedDB is the W3C-standard replacement for deprecated Web SQL. It is supported in Chrome, Firefox, Safari, and Edge, making it the default choice for client-side structured storage in 2026.
💡
Beginner Tip
Always feature-detect with if (!window.indexedDB) before opening a database. Private browsing modes and very old browsers may not expose the API.
Capabilities
Key Features of IndexedDB
Structured data storage — save plain objects, arrays, Date values, ArrayBuffer, Blob, and File instances.
Indexing — create indexes on object properties for fast lookups without scanning every record.
Transactions — group multiple operations into one atomic unit; all succeed or all roll back.
Asynchronous API — event-driven requests (onsuccess, onerror) keep the UI responsive. Modern code can also use the promise-based indexedDB.databases() and wrapper libraries.
Origin-scoped persistence — data survives page reloads until the user clears site data or storage quota is exceeded.
Large capacity — browsers typically allow hundreds of megabytes per origin (varies by device and free disk space).
Vocabulary
Basic Concepts
Before writing code, learn these terms—they appear in every IndexedDB program:
Database — the top-level container (opened with indexedDB.open(name, version)). One origin can have many databases.
Object store — a collection of records, like a table without fixed columns. Each record is a JavaScript object.
Key / keyPath — the unique identifier for a record. Set keyPath: 'id' on the store, or use autoIncrement: true to generate numeric keys.
Index — a secondary lookup path on a property (e.g. index on email for fast searches by email).
Transaction — a scope for reading or writing one or more object stores. Modes: readonly or readwrite.
Cursor — an iterator that walks records one at a time with cursor.continue().
Version upgrade — the only time you can create, rename, or delete object stores—inside onupgradeneeded when the version number increases.
Setup
Creating and Opening a Database
Call indexedDB.open('myDatabase', version). The second argument is a version number (integer). On first open, version 1 creates the database. To add a new object store later, bump the version and handle onupgradeneeded:
js
let db;
const request = indexedDB.open('myDatabase', 1);
request.onupgradeneeded = function (event) {
db = event.target.result;
const objectStore = db.createObjectStore('myObjectStore', { keyPath: 'id' });
objectStore.createIndex('nameIndex', 'name', { unique: false });
};
request.onsuccess = function (event) {
db = event.target.result;
console.log('Database ready');
};
request.onerror = function (event) {
console.error('Database error:', event.target.error.message);
};
onupgradeneeded runs beforeonsuccess when the version changes. Create all stores and indexes there—never inside a normal transaction on first visit.
Storage
Working with Object Stores
Object stores hold your records. To add data, start a readwrite transaction, get the store, and call add() or put():
See the dedicated INSERT tutorial for add() vs put(), auto-increment keys, and storing blobs.
Reading Data
Transactions and Cursors
Transactions ensure operations complete as a unit. To read every record, open a readonly transaction and use openCursor():
js
function getAllItems() {
const transaction = db.transaction(['myObjectStore'], 'readonly');
const objectStore = transaction.objectStore('myObjectStore');
const request = objectStore.openCursor();
request.onsuccess = function (event) {
const cursor = event.target.result;
if (cursor) {
console.log('Item:', cursor.value);
cursor.continue();
} else {
console.log('No more items.');
}
};
request.onerror = function (event) {
console.error('Cursor error:', event.target.error.message);
};
}
For a single key, store.get(key) is simpler. For many keys at once, getAll() returns an array. The Retrieve tutorial covers indexes and filtered cursors in depth.
Reliability
Handling Errors
Every IndexedDB request can fail—quota exceeded, duplicate key on add(), or invalid transaction. Attach onerror handlers and read event.target.error.message (not the legacy errorCode property):
js
request.onerror = function (event) {
console.error('Error:', event.target.error.message);
};
transaction.onabort = function () {
console.warn('Transaction aborted — no changes were saved.');
};
transaction.oncomplete = function () {
console.log('Transaction committed successfully.');
};
Also listen for db.onversionchange when another tab upgrades the database—close your connection so the upgrade can finish.
indexedDB.open returns an IDBOpenDBRequest. Schema setup belongs in onupgradeneeded—it fires on first create and whenever you increase the version number.
Example 2 — Add an Item
Insert a contact inside a readwrite transaction. Use add() when the key must not already exist.
Coming from Web SQL? IndexedDB is the modern, cross-browser replacement.
Compatibility
Universal Browser Support
IndexedDB is supported in all major desktop and mobile browsers: Chrome, Firefox, Safari, and Edge. It is a W3C standard and the recommended API for client-side structured storage.
✓ Baseline · Since HTML
IndexedDB API
IndexedDB is supported in all major desktop and mobile browsers: Chrome, Firefox, Safari, and Edge. It is a W3C standard and the recommended API for client-side structured storage.
96%Modern browser support
Google ChromeAll versions · Desktop & Mobile
Full support
Mozilla FirefoxAll versions · Desktop & Mobile
Full support
Apple SafariAll versions · macOS & iOS
Full support
Microsoft EdgeAll versions · Chromium & Legacy
Full support
Internet ExplorerIE 6+ · Legacy environments
Full support
OperaAll modern versions
Full support
IndexedDB APIExcellent
Bottom line: Safe to use for new projects in 2026. Always feature-detect for defense in depth.
Wrap Up
Conclusion
IndexedDB gives web applications a robust way to store structured data on the client. Open a database with indexedDB.open(), define object stores during onupgradeneeded, and perform every read or write inside a transaction. Cursors and indexes make large datasets manageable without blocking the UI.
You now have the conceptual foundation. Continue with the CRUD tutorials in the index above—DELETE, INSERT, Retrieve, and UPDATE—each with five examples and interactive Try It editors.
Use these points when building offline-capable web apps.
6
Core concepts
🗃️01
Object stores
Not SQL tables.
Model
🔄02
Transactions
Every operation.
Required
📈03
onupgradeneeded
Schema only.
Setup
🔍04
Indexes
Fast lookups.
Query
⚡05
Async
Non-blocking.
Performance
📚06
CRUD hub
Go deeper.
Next
❓ Frequently Asked Questions
IndexedDB is a browser API for storing large amounts of structured data on the client. You open a database with indexedDB.open(), save JavaScript objects in object stores, and read or change them inside transactions. It works asynchronously so the page stays responsive.
localStorage stores simple key-value strings with a small size limit. IndexedDB stores objects, blobs, and files, supports indexes for fast lookups, and handles much larger datasets. Use localStorage for a few settings; use IndexedDB for offline apps and rich data.
The onupgradeneeded event runs when you open a database with a higher version number than before. Inside it you create or delete object stores and indexes. Normal add/get/delete operations happen later in readwrite or readonly transactions—not during upgrade.
add() inserts a new record and fails if the key already exists. put() inserts or replaces the record with the same key (upsert). Use add() when duplicates must be rejected; use put() for updates and idempotent saves.
IndexedDB groups work into transactions for consistency and concurrency. A readwrite transaction commits all writes together or rolls back on error. You request a transaction on one or more object store names, then call methods on transaction.objectStore('name').
Read the introduction and core concepts, try Example 1 in the gallery or the Try It editor, then open the CRUD tutorial index below. Start with DELETE or INSERT depending on whether you learn better by removing or adding data first.
Did you know?
IndexedDB runs in a separate browser thread from your JavaScript. That is why every operation is asynchronous—your main thread schedules work and receives results through events, keeping scroll and animations smooth even when reading thousands of records.