IndexedDBShim: one storage API where the browser implementations differ
A polyfill for IndexedDB using WebSql
At a glance
- What is it?
- IndexedDBShim is a JavaScript polyfill that implements IndexedDB on top of WebSQL, and it can also replace a browser's own IndexedDB when that implementation is broken or inconsistent. It suits a team supporting older browsers, and it inherits the limitations of WebSQL, which is the trade at the centre of the project.
- Who is it for?
- Adopt IndexedDBShim when you support a browser without IndexedDB or one whose implementation misbehaves, because the shim gives you one indexable offline storage API with consistent error handling, transaction timing and cursor behaviour across environments. Do not adopt it in a new project that only targets current browsers, since the shim is a compatibility layer and the native API is better supported.
- 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 26 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why a polyfill that can also replace a native implementation
IndexedDBShim is a polyfill for IndexedDB using WebSQL, and the pitch in the README is a single, indexable, offline storage API across all desktop and mobile browsers and Node.js.
The interesting part is the second half of the design. The README says that even if a browser natively supports IndexedDB, you may still want to use this shim, and gives two reasons. Some native implementations are very buggy, with a link to a post about IndexedDB being broken on iOS 8. Others are missing certain features. And there are many minor inconsistencies between different browser implementations, such as how errors are handled, how transaction timing works, how records are sorted, how cursors behave. Using this shim ensures consistent behaviour across all browsers.
So there are three modes, and the feature list spells them out. It optionally adds full IndexedDB support to any web browser that supports WebSQL. It does nothing if the browser already natively supports IndexedDB. And it can optionally replace native IndexedDB on browsers with buggy implementations.
That third mode is what makes the project more than a legacy compatibility shim. The inconsistencies it fixes, error semantics and transaction timing in particular, are the kind of thing that produces a bug report on one browser and not another,
The primary language is JavaScript, the npm package is indexeddbshim at version 19.0.7, and the last push to the main branch was on 2026-09-27. The project is under the indexeddbshim organisation, and the author is Parashuram with Brett Zamir as a contributor.
Installing it, and the SQLite build on a Mac
There are three ways in. You can download the development script or the production minified script from the repository, or install it from npm or yarn.
npm install indexeddbshimyarn add indexeddbshimThe Mac caveat is the one that catches people out, and it exists because the Node path goes through a native SQLite3 binding. You may need CMake installed for the SQLite3 install to work, and you need to build SQLite3 from source via `npm install --build-from-source` in the node-sqlite3 directory. The README also says to make sure Python 2.7 is installed.
That is a real cost of the Node.js support, and it is worth understanding why it is there. The Node path works courtesy of the websql package, which sits on top of SQLite3, so an environment without IndexedDB and without a browser still gets a working implementation, but it needs a compiled database engine underneath it.
The browser path has no such requirement, because WebSQL is built into the browser.
The package manifest also shows the quality bar the project holds itself to. The coverage configuration requires 100 percent on lines, statements, functions and branches, with a check-coverage flag, and there is a separate c8 exclude list for node_modules, the web-platform-tests directory and the test directories. The test scripts include a mocha runner configuration, a polyfill test suite and a web-platform-tests directory checked out as a submodule, which is a strong signal about how the implementation is verified: against the upstream specification test suite rather than against hand written expectations.
Setting it up in a browser, in Node and in Deno
The three environments need three different setup paths, and the browser one is the simplest.
In a browser you add a script tag:
<script src="./node_modules/indexeddbshim/dist/indexeddbshim.min.js"></script>If you need full Unicode compliance, handling special non-alphanumeric identifiers in store and index names, you use the other build instead:
<script src="./node_modules/indexeddbshim/dist/indexeddbshim-UnicodeIdentifiers.min.js"></script>In Node.js you call setGlobalVars yourself, and the two lines you set up are the ones that trip people:
const setGlobalVars = require('indexeddbshim');
globalThis.window = globalThis; // We'll allow ourselves to use `window.indexedDB` or `indexedDB` as a global
globalThis.location = {origin: 'mysite.com'}; // Set `origin` to anything besides the string `"null"`
setGlobalVars(); // See signature belowThe window assignment lets you use either window.indexedDB or a bare indexedDB as a global, and the location assignment needs an origin that is not the string null, because a null origin is what the browser uses for sandboxed contexts and the shim uses the origin to namespace its storage.
Deno needs the location passed on the command line:
deno --location http:foo -A main.tsand the location must start with http: or https: per Deno. The README also notes that the project works with Deno and with idb, according to a user.
The Jest brand check, and the workaround that has to run early
The documentation covers one failure in unusual detail, and the detail is the point: if you call setGlobalVars(global) inside a Jest test or setup file, accessing indexedDB afterwards can throw a TypeError reading Illegal invocation.
The reason is described precisely. The indexedDB getter performs a brand check, much like native browsers do, to ensure it is invoked with the same this it was defined on. Under Jest, the object accessing indexedDB, for example jsdom's Window, is not always reference-equal to the object passed to setGlobalVars, so the check fails.
The workaround is to set shimNS on the target global before calling setGlobalVars, so the brand check is bypassed for that object:
// e.g., jest/testSetup.js
import setGlobalVars from 'indexeddbshim';
global.shimNS = true;
setGlobalVars(global);The harder case is documented in a separate section, headed with the situation you will actually hit. You can hit the error even if your test never touches indexedDB directly, for example in a plain component render test. It means some module in your import chain, a dependency or a polyfill file, pulls in the invasive build of indexeddbshim, which auto-invokes setGlobalVars as a side effect of being imported.
Because that happens at import time, you must set shimNS on the global before that import chain runs, and a normal test file beforeEach or import is too late. The fix is Jest's setupFiles, which runs before the test framework and before any test file imports are evaluated:
// jest.config.js
module.exports = {
preset: 'jest-expo',
setupFiles: ['./jest/shim-ns-setup.js']
};// jest/shim-ns-setup.js
if (typeof global.window === 'undefined') {
global.window = global;
}
global.window.shimNS = true;
global.shimNS = true;And the note about setupFilesAfterEnv says that if you are using that instead, for example because you also need the @testing-library/jest-native matchers, it will be too late if the offending import happens as part of loading. The distinction between setupFiles and setupFilesAfterEnv is the whole difference between working and not working here, and it is a detail that only surfaces once something in your dependency tree pulls the shim in implicitly.
What the repository contains, and how the licences work
The repository layout shows a project with a lot of moving parts behind one small dependency.
There is src/ for the source, dist/ for the built output, typings/ for the type definitions, docs/ for the documentation, tests-mocha/, tests-polyfill/ and test-support/ for the three test suites, examples/ with an IDBKeyUtils.html and a dbjs.html page, and a web-platform-tests directory, which is a submodule holding the upstream specification test suite. The build is Rollup with TypeScript, driven by rollup.config.js, and there are separate tsconfig.json and tsconfig-prod.json files for development and production compilation.
The licences are the other thing worth noting, and they are handled with some care. The package manifest declares the licence as (MIT OR Apache-2.0), a dual licence, and the repository root carries both LICENSE-APACHE.txt and LICENSE-MIT.txt, alongside an IndexedDBShim.nuspec and a licenseInfo.json. The project generates its own licence badges with license-badger, one for the production dependency tree and one for all development dependencies, and the prepublish hook runs those badge generators and the build before anything is published.
That is a small sign of a project that takes distribution seriously, which fits a library whose whole job is to be dropped into other people's pages and then maintained by them.
Editorial conclusion
Adopt IndexedDBShim when you support a browser without IndexedDB or one whose implementation misbehaves, because the shim gives you one indexable offline storage API with consistent error handling, transaction timing and cursor behaviour across environments. Do not adopt it in a new project that only targets current browsers, since the shim is a compatibility layer and the native API is better supported. Before you use it, check whether WebSQL is available in your target browsers at all, because the shim needs it, and read the Jest section of the documentation, since the illegal invocation error there comes from a brand check that has a documented workaround.
Frequently asked questions
What does IndexedDBShim do and when would I use it?
It is a polyfill for IndexedDB using WebSQL, and it can also optionally replace a browser's native IndexedDB when that implementation is buggy or missing features. The README points at broken native implementations, missing features, and inconsistencies in error handling, transaction timing, record sorting and cursor behaviour between browsers, and says using the shim ensures consistent behaviour across all browsers.
How do I install IndexedDBShim?
Run `npm install indexeddbshim` or `yarn add indexeddbshim`, or download the development or minified production script from the repository. On a Mac you may need CMake for the SQLite3 install to work, building SQLite3 from source with `npm install --build-from-source` in the node-sqlite3 directory, and Python 2.7 installed.
How do I set up IndexedDBShim in Node.js or Deno?
In Node.js, require the package, assign globalThis.window = globalThis so you can use window.indexedDB or a bare indexedDB, assign globalThis.location with an origin that is not the string "null", then call setGlobalVars(). In Deno, pass the location on the command line with `deno --location http:foo -A main.ts`, and the location must start with http: or https:.
Why does indexedDB throw Illegal invocation in my Jest tests?
The indexedDB getter performs a brand check to ensure it is invoked with the same this it was defined on, and under Jest the object accessing indexedDB, such as jsdom's Window, is not always reference-equal to the object passed to setGlobalVars. Set shimNS on the target global before calling setGlobalVars, and if a dependency pulls in the shim implicitly, use Jest's setupFiles so the flag is set before the import chain runs.
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/indexeddbshim-indexeddbshim)Was this useful?