IndexedDB is a low-level browser API for storing large amounts of structured data on the client. Unlike localStorage, it supports indexes, transactions, and complex JavaScript objects—including files and blobs. It is the modern replacement for the deprecated Web SQL API.
This tutorial focuses specifically on inserting data into IndexedDB: opening a database, creating an object store, running add() and put() inside readwrite transactions, and handling success and errors safely.
What You’ll Learn
01
indexedDB.open
Open or upgrade DB.
02
Object store
Define schema.
03
add()
New records.
04
put()
Upsert rows.
05
Transactions
readwrite mode.
06
Errors
Callbacks & abort.
Fundamentals
What Is IndexedDB?
IndexedDB is a transactional, key-value database built into modern browsers. Data is organized in object stores (similar to tables) inside a named database. Each record is a JavaScript object; you choose a keyPath (like id) or let the browser auto-generate keys with autoIncrement: true.
The API is asynchronous—operations use requests and event callbacks instead of blocking the main thread. That keeps your UI responsive while large datasets are read or written.
💡
Beginner Tip
Think of IndexedDB like a NoSQL database in the browser. You store whole JavaScript objects, not SQL rows. To insert, you open the database, start a readwrite transaction, and call objectStore.add(item).
Setup
Creating an IndexedDB Database
Before inserting data, open the database and define an object store where records will live. Schema changes happen only in onupgradeneeded when you bump the version number.
js
const request = indexedDB.open('myDatabase', 1);
request.onupgradeneeded = function (event) {
const db = event.target.result;
const store = db.createObjectStore('contacts', {
keyPath: 'id',
autoIncrement: true
});
store.createIndex('emailIndex', 'email', { unique: false });
};
request.onsuccess = function (event) {
const db = event.target.result;
console.log('Database ready');
};
request.onerror = function (event) {
console.error('Open failed:', event.target.error.message);
};
indexedDB.open(name, version) — opens an existing database or creates a new one.
onupgradeneeded — runs when the version increases; create object stores and indexes here.
keyPath: 'id' — tells IndexedDB which property is the primary key.
autoIncrement: true — generates numeric ids when you omit id on insert.
createIndex — optional secondary lookup on email (used later for queries).
Connection
Opening a Database
After the first setup, you open the same database on every page load. If the version matches, onupgradeneeded does not run—you go straight to onsuccess.
js
const request = indexedDB.open('myDatabase', 1);
request.onsuccess = function (event) {
const db = event.target.result;
// db is ready — start transactions here
};
request.onerror = function (event) {
console.error('Database error:', event.target.error.message);
};
Keep a reference to db in a variable or module. Close it with db.close() when your app unloads if you manage connections manually.
Core Topic
Inserting Data
To insert a record, start a readwrite transaction on the object store, then call add() with a plain JavaScript object:
js
const request = indexedDB.open('myDatabase', 1);
request.onsuccess = function (event) {
const db = event.target.result;
const tx = db.transaction(['contacts'], 'readwrite');
const store = tx.objectStore('contacts');
const item = { name: 'John Doe', age: 30 };
const addRequest = store.add(item);
addRequest.onsuccess = function () {
console.log('Added with id:', addRequest.result);
};
addRequest.onerror = function (event) {
console.error('Add failed:', event.target.error.message);
};
};
With autoIncrement: true, you can omit id—IndexedDB assigns one and returns it in addRequest.result. If you provide an id that already exists, add() fails with a ConstraintError.
add() vs put()
store.add(item) — insert only; fails if the key already exists.
store.put(item) — insert or replace; updates an existing record with the same key.
Reliability
Handling Transactions
Transactions group one or more operations. For inserts, always use 'readwrite' mode and listen for completion and errors:
If any request in the transaction fails and the error is not handled, the transaction aborts and none of its writes are saved. Chain multiple add() calls in one transaction for atomic batch inserts.
Reliability
Error Handling
IndexedDB errors can occur when opening the database, exceeding storage quotas, using a closed connection, or violating key constraints. Handle errors at both the request and transaction levels:
Storage is per-origin; other sites cannot read your IndexedDB data.
For reading inserted data, continue to the Retrieve tutorial.
Compatibility
Universal Browser Support
IndexedDB is supported in all modern browsers: Chrome, Firefox, Safari, Edge, and mobile WebViews. It is the W3C-recommended API for structured client-side storage, unlike the deprecated Web SQL API.
✓ Baseline · Since HTML
IndexedDB API
IndexedDB is supported in all modern browsers: Chrome, Firefox, Safari, Edge, and mobile WebViews. It is the W3C-recommended API for structured client-side storage, unlike the deprecated Web SQL API.
95%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 IndexedDB for new cross-browser web apps. Feature-detect with window.indexedDB if you need to support very old environments.
Pro Tips
💡 Best Practices
✅ Do
Define schema in onupgradeneeded with a clear version strategy
Validate objects before add() or put()
Handle request and transaction error callbacks
Use put() for save/upsert and add() for strict inserts
Batch related writes in a single transaction
Wrap callback APIs in Promises for cleaner async code
❌ Don’t
Create object stores outside onupgradeneeded
Assume add() succeeds without an error handler
Store functions, DOM nodes, or non-cloneable values
Open a new database connection for every single insert
Forget to bump the version when adding new stores
Rely on localStorage for large structured datasets
Wrap Up
Conclusion
Inserting data into IndexedDB means opening a database, ensuring your object store exists (in onupgradeneeded), and calling add() or put() inside a readwrite transaction with proper error handling.
IndexedDB is the right choice for modern offline-capable web apps. Once you can insert records confidently, learn how to read them in the Retrieve tutorial and how to change them in UPDATE.
Use these points when adding records to a client-side database.
5
Core concepts
🗃️01
Upgrade
Create stores.
Schema
➕02
add()
New keys.
Insert
🔄03
put()
Upsert.
Update
🔒04
readwrite
Tx mode.
Required
⚠️05
onerror
Handle fail.
Reliability
❓ Frequently Asked Questions
Open the database with indexedDB.open(), create an object store in onupgradeneeded if needed, then start a readwrite transaction: db.transaction(['storeName'], 'readwrite'). Get the object store and call store.add(object) for new records or store.put(object) to insert or update.
add() fails with a ConstraintError if a record with the same key already exists. put() writes the record regardless—creating it if missing or replacing the existing one. Use add() for strict inserts and put() for upsert-style saves.
IndexedDB groups reads and writes in transactions for consistency. A readwrite transaction is required for add() and put(). If the transaction aborts, none of its writes are committed.
Create object stores only inside the onupgradeneeded event when you open the database with a higher version number. Schema changes cannot run during a normal readwrite transaction on an already-open database.
Listen to request.onerror on the add/put request and transaction.onerror on the transaction. Read event.target.error.message, show user feedback, and call event.preventDefault() on transaction errors if you want to suppress automatic abort logging in some cases.
Yes. IndexedDB is supported in all current major browsers including Chrome, Firefox, Safari, and Edge. It is the recommended API for structured client-side storage in modern web apps.
Did you know?
IndexedDB uses the structured clone algorithm to copy objects into storage. That is the same mechanism used by postMessage between workers—so plain objects, arrays, Dates, and Blobs work, but functions and DOM elements do not.