npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

tiny-idb

v1.6.0

Published

An extremely fast, super simple, and dependency-free IndexedDB wrapper. A drop-in replacement for localStorage with reliability and durability.

Readme

tiny-idb

A tiny IndexedDB wrapper for simple key/value persistence.

tiny-idb is the small, async alternative to localStorage when you want IndexedDB durability without taking on IndexedDB ceremony. It stays intentionally narrow:

  • automatic multi-store support within a single database
  • promise-based reads and writes
  • atomic read-modify-write helpers
  • cursor-based iteration with filtering and pagination
  • raw() for native IndexedDB escape hatches

NPM Version License Size

Why tiny-idb?

  • Tiny footprint. The minified bundle is about 1KB gzipped.
  • Async API. No synchronous main-thread storage calls.
  • Atomic helpers. update(), push(), and merge() run inside one IndexedDB transaction, avoiding race conditions.
  • Automatic batching. Calls made in the same microtask are pipelined into one transaction by default for massive performance gains.
  • Efficient iteration. Memory-friendly cursor-based scans for entries and pagination.

How it compares

| Feature | localStorage | idb-keyval | localForage | Dexie | tiny-idb | |---------|:---:|:---:|:---:|:---:|:---:| | Size | N/A | ~1KB | ~10KB | ~30KB | ~1KB | | Async | ❌ | ✅ | ✅ | ✅ | ✅ | | Atomic Updates | ❌ | ❌ | ❌ | ✅ | ✅ | | Auto-Batching | ❌ | ❌ | ❌ | ❌ | ✅ | | Pagination | ❌ | ❌ | ❌ | ✅ | ✅ | | Multi-Store | ❌ | ✅ | ✅ | ✅ | ✅ |

Installation

npm install tiny-idb

Running Tests

npm test

Building & Minification

npm run build

API

| Method | Description | |--------|-------------| | open(dbName, storeName?, batching?) | Creates or retrieves a cached instance. storeName defaults to dbName. batching (default true) pipelines calls in the same microtask. | | open(dbName, batching) | Shorthand for open(dbName, dbName, batching). | | get(key) | Returns the value for key, or undefined. | | set(key, value) | Stores value at key. | | remove(key) | Deletes key. | | clear() | Removes every entry in the store. | | keys() | Returns all keys. | | values() | Returns all values. | | entries(filter?) | Returns [key, value] pairs. filter is called with (value, key). | | paginate(limit, start?, dir?, filter?) | Returns { items, next } for paginated scans. | | count() | Returns the number of entries. | | has(key) | Returns true if key exists. | | raw(cb, mode?) | Runs cb with the native IDBObjectStore. | | update(key, fn) | Atomically replaces a value using fn(currentValue). | | push(key, value) | Atomically appends value to an array. | | merge(key, patch) | Atomically shallow-merges a non-null object patch. | | getItem(key) | Alias for get(key). | | setItem(key, value) | Alias for set(key, value). | | removeItem(key) | Alias for remove(key). | | MODE_RO | Constant for raw() mode: 'readonly'. | | MODE_RW | Constant for raw() mode: 'readwrite'. | | DIR_NEXT | Constant for paginate() direction: 'next'. | | DIR_PREV | Constant for paginate() direction: 'prev'. |

Guarantees

  • set, remove, clear, update, push, and merge run inside IndexedDB transactions.
  • update, push, and merge avoid the usual read-modify-write race.
  • Batched calls in the same microtask share one transaction unless batching is disabled.
  • merge() rejects invalid patches instead of coercing them.
  • Callbacks passed to update() and raw() must stay within the transaction lifetime.

Browser Support

Supported by virtually all browsers in use today (99%+ market share). Since May 2018, this feature works across the latest devices and major browser versions:

  • Chrome 71+
  • Firefox 69+
  • Safari 12.1+
  • Edge 79+

Development

tiny-idb is written in pure vanilla JavaScript. No compilation is required for development.

Usage

You can use either the default export or the named tinyIDB export. They are identical:

import db from 'tiny-idb';
// OR
import { tinyIDB as db } from 'tiny-idb';

localStorage-style calls

await db.setItem('session_id', 'xyz-123');
const sessionId = await db.getItem('session_id');
await db.removeItem('session_id');

Custom database and multi-store

// Separate databases (store name defaults to db name)
const settings = db.open('app-settings');

// Disable automatic microtask batching
const noBatch = db.open('my-db', false);

// Multiple stores in the same database
const users = db.open('my-app', 'users');
const logs = db.open('my-app', 'logs');

await settings.set('theme', 'dark');
await users.set(42, { name: 'Alice' });
await logs.push('recent', 'User 42 logged in');

Atomic counter

await db.set('page_views', 0);
await db.update('page_views', (count) => (count || 0) + 1);

Atomic array append

await db.push('cart', { id: 101, qty: 1 });
await db.push('cart', { id: 202, qty: 2 });

Atomic object merge

await db.set('settings', { theme: 'dark', notifications: true });
await db.merge('settings', { notifications: false, language: 'en' });

Automatic Batching (Performance)

One of the most powerful features of tiny-idb is its automatic transaction pipelining. IndexedDB transactions are expensive to open. tiny-idb caches calls made in the same microtask (tick) and executes them in a single transaction.

// These 3 calls happen in the same tick.
// tiny-idb automatically combines them into ONE 'readwrite' transaction.
// This is 10x-100x faster than 3 separate transactions.
db.set('user', 'alice');
db.set('theme', 'dark');
db.set('notifications', true);

// You can wait for all at once
await Promise.all([ /* ... promises from above ... */ ]);

TypeScript Support

tiny-idb comes with full TypeScript definitions. You can use generics to type your retrieved data:

interface User {
  name: string;
  age: number;
}

// Typing a single retrieval
const user = await db.get<User>('user:1');

// Typing iteration
const users = await db.entries<string, User>();
// users is [string, User][]

// Typing pagination
const page = await db.paginate<string, User>(10);
// page.items is [string, User][]

Binary data

const blob = await (await fetch('/profile.jpg')).blob();
await db.set('user_avatar', blob);

Filtered Iteration

// Scans and returns only users over 18
const isAdult = (user) => user.age >= 18;
const adults = await db.entries(isAdult);

// Scans only keys with a specific prefix
const isLog = (val, key) => key.startsWith('log:');
const logs = await db.entries(isLog);

Pagination

import db from 'tiny-idb';

// 1. Fetch first 10 items
const page1 = await db.paginate(10);
console.log(page1.items); // [[key1, val1], [key2, val2], ...]

// 2. Fetch next 10 items starting from where we left off
// Tip: 'next' is the exact key to start the next page with (no duplicates).
if (page1.next) {
  const page2 = await db.paginate(10, page1.next);
}

// 3. Reverse pagination (fetch 10 newest items)
const { items: latest } = await db.paginate(10, null, db.DIR_PREV);

// 4. Pagination with a filter and direction
// Define filter separately for readability (e.g. items over $120)
const isExpensive = (item) => item.price > 120;
const { items: premiumItems } = await db.paginate(10, null, db.DIR_PREV, isExpensive);

update() and raw() callback rules

The callback must finish while the transaction is still alive.

Safe:

  • Synchronous logic.
  • Microtask-only async such as Promise.resolve(...).
  • Returning an IndexedDB request promise (e.g. from store.get() or store.put()).

Unsafe:

  • setTimeout or setInterval.
  • Network requests (fetch).
  • Long await chains unrelated to the current transaction.

Recipes

Prefix scan

import { tinyIDB as db } from 'tiny-idb';

const todos = await db.raw((store) => new Promise((resolve, reject) => {
  const range = IDBKeyRange.bound('todo:', 'todo:\uffff');
  const req = store.openCursor(range);
  const matches = [];
  req.onsuccess = () => {
    const cursor = req.result;
    if (!cursor) return resolve(matches);
    matches.push([cursor.key, cursor.value]);
    cursor.continue();
  };
}));

Custom Key Range

import { tinyIDB as db } from 'tiny-idb';

// Paginate only within a specific key range
// Accepts both a starting key or a full IDBKeyRange
const range = IDBKeyRange.bound('user:a', 'user:f');
const { items } = await db.paginate(20, range);

Direct Write with raw()

import { tinyIDB as db } from 'tiny-idb';

// Direct write without manual promise wrapping. 
// raw() resolves only after the transaction completes.
await db.raw((store) => store.put('value', 'key'), db.MODE_RW);

License

MIT © Jelodar