mrkeyoor.com_
Sat 08 Aug 17:42 UTC
npmWeb Frontendupdated 08 Aug 2026

idb-keyval

idb-keyval is a tiny promise-based key/value layer over the browser's IndexedDB database. It gives you get, set, delete, batch, update, and enumeration helpers without making you manage requests, transactions, object stores, or event handlers directly. Values can be anything supported by structured cloning, including objects, arrays, dates, and blobs. It is deliberately not a general database wrapper: there are no indexes, queries, migrations, or relationships.

Verdict

Install it for small, browser-only persistence where a key/value API is the whole requirement. Once you need querying, migrations, cross-runtime storage, or synchronization, start with a fuller database layer instead of stretching this one.

API stability5/5The public surface is a short set of promise-returning functions plus the `createStore` factory, and version 6.3.0 still documents the same get, set, delete, batch, update, and enumeration model. The narrow IndexedDB mapping leaves little policy to change, while legacy compat exports remain published alongside modern ESM and CommonJS builds.
Docs4/5The README demonstrates every exported helper, states return behavior for missing keys, explains structured-clone limits, contrasts atomic update with unsafe get-then-set code, and documents legacy bundles. It is unusually useful for a tiny package, though custom stores are split into another file and quota, eviction, SSR, and test-environment behavior receive little guidance.
Maintenance4/5Version 6.3.0 is current on npm, the repository was pushed in July 2026, and it is not archived. The repository shows 24 open issues and pull requests, a manageable queue for a deliberately small wrapper. Release activity is naturally quieter than a framework, so teams needing fast responses to browser edge cases should still check the specific issue before adopting.
Ecosystem4/5The package recorded 7,744,540 npm downloads in the fetched week and the repository has 3,225 stars, strong evidence that this small API is widely exercised. It ships TypeScript declarations and modern and legacy bundle formats, but it intentionally has no adapters, plugins, query layer, synchronization service, or non-browser backend ecosystem.

Use it if

  • You need persistent browser storage for a modest collection of values and localStorage is too limited or synchronous
  • You want IndexedDB durability and structured-clone support without learning its request and transaction APIs
  • You need atomic batch writes or queued updates for counters and other read-modify-write operations
  • You care about a sub-kilobyte gzip cost and want imports that tree-shake down to only the helpers you use
Skip it if

Setup reality

Installation is only `npm install idb-keyval`; there are no dependencies, peer dependencies, credentials, native builds, or config files. Modern bundlers choose the ESM or CommonJS export, and TypeScript declarations ship in the package. The catch is the platform contract. IndexedDB must exist, so server-side rendering code cannot touch the store during render and Node-only tests need a browser or an IndexedDB shim. Every method is asynchronous and can reject for quota, permission, private-mode, serialization, or transaction failures, so writes should be awaited and failures should not disappear into fire-and-forget calls. Values must be structured-clonable; functions, DOM nodes, and other non-cloneable objects will fail. The default database is named `keyval-store` and its object store is `keyval`; call `createStore` when applications on the same origin need isolation, and note that one database cannot hold two custom stores. `get` returning undefined means either no record or an explicitly stored undefined, which can make cache logic ambiguous. For IE 10 or 11 the README requires `idb-keyval/compat` plus a Promise polyfill, and its notes call out older Edge and IE limitations around null and array keys. Finally, a `get` followed by `set` is not an atomic update. Use `update` or the batch helpers when concurrent callers can touch the same key.

Patterns

Store a structured-clone valuestore-value

import { set } from 'idb-keyval';

await set('profile', { name: 'Ada', seenAt: new Date() });

Await the promise so quota, permission, and serialization failures reach your error handling.

Read a valueread-value

import { get } from 'idb-keyval';

const profile = await get('profile');
if (profile === undefined) {
  console.log('not cached');
}

A missing key resolves to undefined; storing undefined makes absence indistinguishable from that stored value.

Delete one keydelete-value

import { del } from 'idb-keyval';

await del('profile');

Deletion is asynchronous even when the key does not exist.

Write several values atomicallywrite-many

import { setMany } from 'idb-keyval';

await setMany([
  ['theme', 'dark'],
  ['page-size', 50],
]);

setMany uses one transaction; if one pair cannot be added, none of the pairs are committed.

Read several keys in one transactionread-many

import { getMany } from 'idb-keyval';

const [theme, pageSize] = await getMany(['theme', 'page-size']);

The result order matches the key order, and missing records appear as undefined entries.

Increment without a lost updateupdate-atomically

import { update } from 'idb-keyval';

await update('launch-count', (value = 0) => value + 1);

Use update for read-modify-write logic; separate get and set calls can race and overwrite each other.

Delete several keys togetherdelete-many

import { delMany } from 'idb-keyval';

await delMany(['draft', 'draft-saved-at']);

delMany is faster than scheduling separate del calls because it shares one transaction.

Clear the active storeclear-store

import { clear } from 'idb-keyval';

await clear();

With the default store this removes every idb-keyval record for the origin, so do not use it as a one-user logout unless all values are user-specific.

List keys and valueslist-entries

import { entries } from 'idb-keyval';

for (const [key, value] of await entries()) {
  console.log(key, value);
}

This enumerates the whole store; it is not an indexed query and can be expensive for large datasets.

List all keyslist-keys

import { keys } from 'idb-keyval';

const cacheKeys = await keys();

Keys may be strings, numbers, or dates; older IE does not support IndexedDB array keys.

Isolate data in a named storecustom-store

import { createStore, get, set } from 'idb-keyval';

const drafts = createStore('editor-db', 'drafts');
await set('post-42', { title: 'Draft' }, drafts);
const draft = await get('post-42', drafts);

Pass the same store handle as the final argument to every helper. `createStore` cannot create two stores inside one database, so use a separate database name per store.

Load the compatibility buildlegacy-browser-build

import 'es6-promise/auto';
import { get, set } from 'idb-keyval/compat';

await set('answer', 42);
console.log(await get('answer'));

The README requires both the compat entry point and a Promise polyfill for IE 10 and 11; this does not fix every old IndexedDB quirk.

Alternatives

PackageRegistryPick it when
idbnpmYou need real IndexedDB databases with indexes, cursors, transactions, and versioned upgrades
dexienpmYou want a richer database layer with declarative schemas, queries, hooks, and live-query tooling
localforagenpmYou need fallbacks for older browsers where IndexedDB is absent or unreliable