Skip to content

Object Stores and Keys

An object store is IndexedDB’s equivalent of a database table. Each record in an object store is a JavaScript object — but unlike a SQL table, there is no schema enforcement. You can store objects with different shapes in the same store. The only structural rule is how the key is determined.

Think of it this way:

  • A SQL table row has a fixed set of columns.
  • An object store record is any JS object you hand it, plus a key that uniquely identifies it.

You create object stores by calling db.createObjectStore(name, options) on the database instance. This method must be called inside the onupgradeneeded event handler — it is only valid during a version upgrade.

const request = indexedDB.open('my-db', 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
db.createObjectStore('contacts', { keyPath: 'id' });
};

The first argument is the store name. The second argument is an options object controlling how keys work.

When you set keyPath, the store uses a property on the stored object as its key. This is called an in-line key because the key lives inside the object itself.

// Every object you store MUST have an `id` property
db.createObjectStore('contacts', { keyPath: 'id' });
// Valid record:
store.add({ id: 1, name: 'Alice', email: '[email protected]' });
// Invalid — no `id` field, will throw:
store.add({ name: 'Bob' });

If you set autoIncrement: true alongside keyPath, the browser generates the key automatically and writes it into the object at the specified path:

db.createObjectStore('contacts', { keyPath: 'id', autoIncrement: true });
// `id` will be filled in automatically:
store.add({ name: 'Alice' }); // stored as { id: 1, name: 'Alice' }

When you omit keyPath (or set it to null), the key is separate from the object. This is called an out-of-line key. You supply it manually as the second argument to add() or put().

db.createObjectStore('raw-blobs', { keyPath: null });
// You provide the key explicitly:
store.add({ data: '...' }, 'blob-001');

Combine with autoIncrement: true to let the browser generate integer keys without touching the stored object:

db.createObjectStore('logs', { autoIncrement: true });
// No key needed — browser assigns 1, 2, 3, ...
store.add({ level: 'info', message: 'App started' });
ModekeyPathautoIncrementKey source
In-line (explicit)'id'falseObject’s id field
In-line (auto)'id'trueAuto-generated, stored in id
Out-of-line (manual)(none)falseCaller provides key
Out-of-line (auto)(none)trueAuto-generated, not in object

The example below opens a database at version 1 and creates two stores:

  • contacts — in-line key using the id property
  • logs — out-of-line auto-incrementing key

Run it to see both store names printed to the console.

Browser Storage
What option do you pass to createObjectStore to use a property on the stored object as its key?
When is createObjectStore() allowed to be called?
You create a store with { autoIncrement: true } and no keyPath. Where is the generated key stored?