IndexedDB is a transactional, client-side database built into web browsers that stores structured JavaScript values, including objects, arrays, files and blobs, under keys and lets applications query them through indexes. Among Web APIs it is the standard choice when a page must keep large or complex data locally, for example to work offline, cache API responses or hold drafts that survive a reload.
Unlike the simple key-value Web Storage API, IndexedDB is asynchronous, supports transactions and can hold far more data. It is available in all modern browsers, in the main thread as well as in web workers and service workers, which makes it the backbone of most offline-capable progressive web applications.
How IndexedDB organizes data
An IndexedDB database belongs to a single origin, the combination of scheme, host and port, and no other origin can read it. Inside a database, data lives in object stores, which play a role similar to tables, although the stored values are JavaScript objects rather than rows with a fixed schema.
| Concept | Description | Analogy in relational databases |
|---|---|---|
| Database | Named, versioned container per origin | Database schema |
| Object store | Collection of values with keys | Table |
| Key path or key generator | Defines or creates the primary key | Primary key or auto-increment |
| Index | Secondary lookup on a property | Secondary index |
| Transaction | Atomic unit of reads and writes | Transaction |
| Cursor | Iterates over a range of records | Server-side cursor |
Values are copied with the structured clone algorithm, so dates, maps, typed arrays and blobs are stored faithfully, while functions and DOM nodes cannot be stored. Keys must be valid key types: numbers, strings, dates, binary data or arrays of these.
Versions, transactions and the request model
Every database has an integer version number. Calling indexedDB.open(name, version) with a higher version fires the upgradeneeded event, which is the only place where object stores and indexes may be created or deleted. This makes schema changes explicit migrations that run once per client.
All data access happens inside a transaction with a defined scope and mode. Transactions commit automatically when no requests remain pending and control returns to the event loop, which means awaiting unrelated work, such as a network fetch, in the middle of a transaction causes it to close unexpectedly.
| Mode | Allowed operations | Concurrency |
|---|---|---|
| readonly | get, getAll, count, cursors | Many can run in parallel |
| readwrite | Reads plus put, add, delete, clear | Serialized on overlapping stores |
| versionchange | Schema changes during upgrade | Exclusive for the whole database |
The original API is event-based: each operation returns a request object with success and error events. Because this style is verbose, many projects use a thin promise wrapper such as the idb library, or a higher-level library such as Dexie, which keep the same underlying model.
IndexedDB compared with other browser storage
Browsers offer several storage mechanisms, and choosing among them depends on data size, structure and access pattern. IndexedDB is the most capable general-purpose option but also the most complex to use.
| Mechanism | Data model | Access | Typical capacity |
|---|---|---|---|
| IndexedDB | Structured values with indexes | Asynchronous | Large share of free disk |
| localStorage | String key-value pairs | Synchronous | About 5 MB per origin |
| Cache Storage | HTTP request and response pairs | Asynchronous | Shares the origin quota |
| Cookies | Small strings sent to the server | Synchronous | About 4 KB per cookie |
| Origin private file system | Files and directories | Asynchronous and sync in workers | Shares the origin quota |
A useful rule is to keep localStorage for tiny preferences, use Cache Storage for network responses and static assets, and use IndexedDB for application data that needs querying, such as messages, records or offline queues.
Storage limits, eviction and persistence
IndexedDB shares a per-origin quota managed by the browser’s storage manager. Exact limits differ by browser and change over time, but modern Chromium-based browsers and Firefox allow an origin to use a substantial fraction of available disk space, while Safari applies stricter policies and may clear script-written storage for sites a user has not visited for some time.
By default, storage is best-effort: the browser may evict it under disk pressure. An application can call navigator.storage.persist() to request persistent storage, and navigator.storage.estimate() to see current usage and quota. Writing beyond the quota fails with a QuotaExceededError, which robust code must handle.
| Situation | Recommended approach | Reason |
|---|---|---|
| Offline email or notes app | IndexedDB with persistence request | Data loss would hurt users |
| Cached product catalog | IndexedDB, best-effort | Data can be re-downloaded |
| Large media files | Blobs in IndexedDB or the file system API | Avoids memory-heavy encoding |
| Sensitive personal data | Minimal local storage, encrypted if needed | Device access exposes stored data |
Pitfalls and common mistakes
Working with IndexedDB reliably requires attention to lifecycle and concurrency details that simpler storage hides.
- Ignoring versionchange events: an open tab blocks upgrades in another tab unless it closes its connection when asked.
- Awaiting non-database work inside a transaction: the transaction auto-commits and later requests fail.
- Assuming permanence: without persistent storage, data can be evicted, and users can clear site data at any time.
- Storing secrets in plain form: IndexedDB is not encrypted and is readable by any script running on the origin, including injected scripts.
- Loading whole stores into memory: use indexes, key ranges and cursors or getAll with limits instead.
Private browsing modes add another nuance: browsers typically keep IndexedDB in memory only and discard it when the session ends, so applications should treat storage as an enhancement that can disappear.
The short version
Most websites forget everything the moment the connection drops. IndexedDB gives a website a private filing cabinet inside the browser, where it can keep messages, documents or product lists on the user’s own device.
Because the data is stored locally and organized with labels and indexes, the site can find things quickly and keep working without internet access. The cabinet is private to that one website, but it is not a safe: the browser may clear it when space runs low unless the site asks to keep it.
Where it makes a difference
A field inspection company equips its technicians with tablets and a progressive web application for recording elevator inspections in basements and plant rooms where mobile coverage is often missing. Each inspection contains about forty checklist answers and several photos.
The application stores every inspection in an IndexedDB object store with an index on status, and keeps the photos as blobs in a second store. A service worker watches for connectivity and uploads records marked as pending, then updates their status in a readwrite transaction. On first launch the app requests persistent storage to protect unsent work. Technicians can now complete a full day of inspections offline, and data is synchronized automatically once they return to coverage, instead of being typed in again from paper notes.