idb-keyval: a 295-byte IndexedDB keyval store
A super-simple-small promise-based keyval store implemented with IndexedDB
At a glance
- What is it?
- idb-keyval wraps IndexedDB in a promise-based get/set API that the README measures at 295 bytes brotli'd for get and set alone. It is a key-value store and nothing more, so the question is whether your storage needs stop there.
- Who is it for?
- Adopt idb-keyval when you need client-side key-value storage and nothing more: a cache, a draft, a preference, a counter updated through update(). Do not adopt it if you need indexes, cursors, range queries or a schema you will migrate; the README points those cases at IDB on NPM, and localForage remains the option when old browsers with broken IndexedDB matter.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 90 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 6, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem idb-keyval solves, and who it is for
IndexedDB is asynchronous, event-based and verbose. Opening a database, waiting for the upgrade event, starting a transaction, issuing a request and reading the result is a lot of ceremony for storing a string. idb-keyval replaces that with promises: set('hello', 'world') and get('hello'). The README describes it as "a super-simple promise-based keyval store implemented with IndexedDB," originally based on Mozilla's async-storage.
The target audience is narrow by design. If your client-side storage need is a key mapped to a value, and you never need to ask the database a question about those values, this library is the shortest path. It is also aimed at bundle-size-sensitive code: the README states that using only get and set costs 295 bytes brotli'd, and using every method costs 573 bytes. That number is the pitch. Anyone who has shipped localForage, which the README puts at roughly 7k because it supports older browsers with broken or absent IndexedDB implementations, will recognise the trade being offered here.
How the IndexedDB wrapper actually works
The mechanism is deliberately thin. By default every method operates on an IndexedDB database named keyval-store and an object store named keyval. The README states that if you want something different you should consult custom-stores.md, which is a separate file in the repository root rather than part of the main README.
Because the backing store is IndexedDB, values go through structured clone. The README says you can store anything structured-clonable: numbers, arrays, objects, dates, blobs. Keys can be numbers, strings or Dates. IndexedDB itself also allows arrays of those values as keys, but the README notes IE does not support that. There is a further caveat: old Edge does not support null.
All methods return promises. The API surface is small and flat: get, set, del, clear, plus the batch forms setMany, getMany and delMany, plus the read-all forms entries, keys and values. There is no query language, no index declaration and no schema versioning exposed to the caller. The library is the storage primitive, not a database layer. The README is explicit about this boundary, saying it is "only a keyval store" and directing anyone who needs iteration and indexing to the separate idb package on NPM.
Installing idb-keyval from npm and storing your first value
The README recommends npm with a bundler such as webpack, rollup or parcel. The install command is a single package:
npm install idb-keyvalAfter that you import named functions. The README's first example imports get and set directly from the package root:
import { get, set } from 'idb-keyval';Writing a value is one call, and set returns a promise that can be chained:
import { set } from 'idb-keyval';
set('hello', 'world');Reading it back resolves with the stored value. The README notes that if there is no 'hello' key, the resolved value will be undefined:
import { get } from 'idb-keyval';
// logs: "world"
get('hello').then((val) => console.log(val));If you are targeting IE10 or IE11, the README says to use the compat version and import a Promise polyfill, because those browsers lack a native Promise:
// Import a Promise polyfill
import 'es6-promise/auto';
import { get, set } from 'idb-keyval/compat';The package also exposes idb-keyval/dist/index.js as the EcmaScript module and idb-keyval/dist/index.cjs as the CommonJS module, for bundlers that need to be forced one way or the other. For a script tag without a build step, the README gives a jsDelivr URL for the UMD build and a module import from the same CDN.
update() and the read-modify-write race the README warns about
The most interesting design decision in idb-keyval is update(). The README spends a section explaining why get followed by set is unsafe for transformations. Both operations are async and non-atomic. Two concurrent increments written as get('counter').then(val => set('counter', (val || 0) + 1)) will both read undefined first, and both will then write 1. The counter ends at 1, not 2, and no error is raised.
update() exists to serialise those transformations. The README's example calls update('counter', val => (val || 0) + 1) twice, and states that the updates queue automatically, so the first sets the counter to 1 and the second to 2. The important word is queue: this is ordering within the library's own scheduling, not an IndexedDB transaction spanning multiple keys.
That distinction matters when you reason about correctness. The README also documents that setMany is atomic, stating that if one of the pairs cannot be added, none will be added. So the library does give you an atomic multi-key write. What it does not give you is a way to compose a read and a write across different keys into one transaction. If your invariant spans two keys, setMany can write both together, but you cannot read one, decide, and write the other atomically. That is the ceiling of this API, and it is worth knowing before you design around it.
Where idb-keyval is the wrong tool
The clearest limitation is stated by the project itself: this is only a key-value store. There are no indexes, so you cannot query by value or by a secondary attribute. entries() returns every entry in the store, which means filtering by value happens in JavaScript after the whole store has been read into memory. For a handful of records that is fine. For thousands, you have built a full scan and called it a lookup.
There is no schema migration story in the README. The default database is keyval-store with object store keyval, and custom stores are documented elsewhere, but nothing in the main README describes versioning your object stores or handling an upgrade path. If your data model is going to change shape over time, you are on your own.
Browser support is another boundary. The compat build exists precisely because older browsers need transpilation and a Promise polyfill, and the README notes that old Edge does not support null as a value. localForage exists because some browsers have broken or absent IndexedDB implementations. If those browsers are in your support matrix, idb-keyval's size advantage is bought with a support gap you will have to fill yourself. Finally, storage is per-origin and subject to eviction. The README does not address persistence guarantees or quota behaviour, so do not read this library as a durability promise.
idb-keyval versus localForage and versus idb
The README names both alternatives, and the difference in approach is size against capability.
localForage offers similar functionality but targets older browsers with broken or absent IndexedDB implementations, and the README puts it at roughly 7k against idb-keyval's 295 bytes for get and set. That gap is not an accident of implementation; it is the cost of a compatibility layer. localForage also presents a localStorage-like API and can fall back to other storage backends, which is a different product decision from binding directly to IndexedDB.
idb, also on NPM, is the opposite trade. The README describes it as a little heavier at 1k and says its first README example is how to create a keyval store, which tells you the relationship plainly: idb-keyval is the convenience layer you would otherwise write on top of idb. If you need iteration and indexing, idb is the one to read. If you need get and set and nothing else, idb-keyval is the smaller answer.
The honest summary is that these are not competitors so much as points on a spectrum from compatibility to capability to minimalism. idb-keyval sits at the minimal end.
Maintenance, licence and upgrade cost
The repository is not archived, and the last push was on 2026-07-08. That is roughly two and a half months before the date of writing, so the project has seen recent activity, though the README and the repository layout suggest a stable API rather than a fast-moving one. The package version in package.json is 6.3.0.
On upgrades, the published surface is small: one package name, a root export, a /compat export, a /umd export and a set of dist files. The package.json marks the package as type module and sideEffects false, which is what makes tree-shaking work for consumers. The practical upgrade cost is low because there is so little API to break, but the size-tests directory and the lib/size-report.js script in the build step indicate that bundle size is treated as a contract the project measures on every build. If you depend on the size claim, that is where it is enforced.
On licensing, package.json declares Apache-2.0 while the repository metadata carries a NOASSERTION licence classification, and the repository root contains a file named LICENCE. Those signals do not agree, so read the LICENCE file and the package.json field together before you rely on either, and take your own advice on what your project requires. This is not legal advice.
Editorial conclusion
Adopt idb-keyval when you need client-side key-value storage and nothing more: a cache, a draft, a preference, a counter updated through update(). Do not adopt it if you need indexes, cursors, range queries or a schema you will migrate; the README points those cases at IDB on NPM, and localForage remains the option when old browsers with broken IndexedDB matter. Before committing, verify that your target browsers support IndexedDB and structured clone, confirm the null-on-old-Edge caveat does not apply to your data, and check whether you need the compat build plus a Promise polyfill. If your only operation is get and set, the size argument holds; the moment you reach for entries() to emulate a query, you have outgrown the library.
Frequently asked questions
What is idb-keyval?
It is a promise-based key-value store built on IndexedDB, described in its README as "a super-simple promise-based keyval store implemented with IndexedDB." It exposes get, set, del, clear and a handful of batch and read-all helpers, and nothing beyond that.
Does idb-keyval work with TypeScript?
The package ships type declarations: package.json points types at ./dist/index.d.ts and the exports map gives a types entry for the root, the /compat subpath and the /umd subpath. The source language listed for the repository is TypeScript.
How do I install idb-keyval?
The README recommends npm with a bundler, using npm install idb-keyval, then importing named functions such as get and set from the package root. For IE10 and IE11 it says to use idb-keyval/compat together with a Promise polyfill.
Why should I use update() instead of get() and set() for a counter?
Because get and set are async and non-atomic, two concurrent read-modify-write cycles can both read the same starting value and both write the same result. The README states that update() queues the transformations automatically, so successive calls produce 1 then 2.
What is the difference between idb-keyval and idb?
The README calls idb-keyval "only a keyval store" and points anyone who needs iteration and indexing at the idb package on NPM, which it describes as a little heavier at 1k. idb-keyval is the smaller convenience layer; idb is the lower-level wrapper.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/jakearchibald-idb-keyval)