The Fastest IndexedDB wrapper out there.
A fast, simple, single file, zero-dependency wrapper driven by an asynchronous micro-task event loop. Groups reads, writes, and deletes into controlled transactional batches — eliminating write-amplification, context switching, and transaction thrashing.
npm i eventloop_idbor copy/paste the single file at src/index.ts
Compare against other IndexedDB wrappers at idbwrappersbenchmark.vercel.app
import { EventLoop_idb } from 'eventloop_idb';
const db = new EventLoop_idb('my_store');const item = { id: "123_string", name: "john doe", age: 31 }
db.write(item.id, () => item, (success) => { if (success) console.log('saved'); });db.read("123_string", (item) => console.log(item)); // { id: "123_string", name: "john doe", age: 31 }db.delete("123_string", (success) => success && console.log("successfully deleted item 123_string"));db.getAllKeys((allKeys) => console.log(allKeys)); // reads all keys
db.getAllKeys((allKeys) => console.log(allKeys), null, 10); // reads the first 10 keys
const idbKeyRange = [10, 100];
db.getAllKeys((allKeys) => console.log(allKeys), idbKeyRange); // reads all keys within an idb key rangedb.getAll((allItems) => console.log(allItems)); // reads all items
db.getAll((allItems) => console.log(allItems), null, 10); // reads the first 10 items
const idbKeyRange = [10, 100];
db.getAll((allItems) => console.log(allItems), idbKeyRange); // reads all items within an idb key rangedb.clear((success) => success && console.log(`${db.name} was successfully cleared`));EventLoop_idb exposes raw Set-based callback collections. Sets do not self-execute on subscription, so always sync the current value before subscribing.
let connectionState = false;
const trackConnection = (connected) => (connectionState = connected);
connectionState = db.readyFlag;
db.onReadyStateChangeClbs.add(trackConnection);db.onReadyStateChangeClbs.delete(trackConnection); // unsubscribe single subscriber
db.onReadyStateChangeClbs.clear(); // unsubscribe all subscribersFires when all pending operations are exhausted and the instance goes idle.
const isIdle = () => console.log(db.name, "is idling");
db.onIdleClbs.add(isIdle);db.onIdleClbs.delete(isIdle); // unsubscribe single subscriber
db.onIdleClbs.clear(); // unsubscribe all subscribersimport { useState, useEffect } from 'react';
import { EventLoop_idb } from 'eventloop_idb';
const db = new EventLoop_idb('my-store');
export function App() {
const [ready, setReady] = useState(db.readyFlag);
const [data, setData] = useState(null);
useEffect(() => {
setReady(db.readyFlag);
const onState = (r) => setReady(r);
db.onReadyStateChangeClbs.add(onState);
return () => db.onReadyStateChangeClbs.delete(onState);
}, []);
const handleWrite = (item) => {
db.write(item.id, () => item, (success) => {
if (success) toast("success");
else toast("fail");
});
};
return (
<div>
<p>{ready ? '🟢 Connected' : '⚪ Connecting...'}</p>
{data && <pre>{JSON.stringify(data, null, 2)}</pre>}
<button onClick={() => handleWrite({ id: "1", name: "john" })}>save</button>
</div>
);
}import { createSignal, onCleanup } from 'solid-js';
import { EventLoop_idb } from 'eventloop_idb';
const db = new EventLoop_idb('my-store');
export function App() {
const [ready, setReady] = createSignal(db.readyFlag);
const [items, setItems] = createSignal([]);
setReady(db.readyFlag);
db.onReadyStateChangeClbs.add(setReady);
onCleanup(() => db.onReadyStateChangeClbs.delete(setReady));
return <p>{ready() ? '🟢 Connected' : '⚪ Connecting...'} — {items().length} records</p>;
}new EventLoop_idb(name: string)| Property | Type | Description |
|---|---|---|
name |
string |
The name of the DB |
db |
IDBDatabase |
Direct access to the raw native IDB instance |
readyFlag |
boolean |
true when open and ready |
readyState |
string |
Detailed connection state: 'done' · 'blocked' · 'closed' · 'unexpectedly closed' · 'close' |
onReadyStateChangeClbs |
Set<(readyFlag: boolean) => void> |
Fires on every connection state transition |
onIdleClbs |
Set<() => void> |
Fires when all pending operations are exhausted and the instance goes idle |
Read.
read(id: string, clb: (res: any) => void): voidRead all keys, or only matching keys if either or both range or count are provided (skip range with null).
getAllKeys(clb: (keys: string[]) => void, range?: IDBKeyRange, count?: number): voidRead all items, or only matching items if either or both range or count are provided (skip range with null).
getAll(clb: (items: any[]) => void, range?: IDBKeyRange, count?: number): voidWrite (takes an accessor () => item, not a direct value).
write(id: string, data: (id: string) => any, clb?: (ok: boolean) => void): voidDelete a single record.
delete(id: string, clb?: (ok: boolean) => void): voidDelete all records.
clear(clb?: (ok: boolean) => void): voidMIT