LocalMode
idb

Overview

Minimal IndexedDB storage adapter using the idb library — only ~3KB.

@localmode/idb

Minimal IndexedDB storage adapter using the idb library. A thin, promise-based wrapper around IndexedDB with typed schemas at only ~3KB.

Features

  • 🪶 ~3KB — Smallest external storage adapter
  • 🔒 Type-Safe — Full TypeScript schema definitions via DBSchema
  • 🧩 Promise-Based — Clean async/await API over raw IndexedDB
  • 🏗️ Typed Indexes — Collection-scoped lookups with typed index definitions

Installation

bash pnpm install @localmode/idb @localmode/core
bash npm install @localmode/idb @localmode/core
bash yarn add @localmode/idb @localmode/core
bash bun add @localmode/idb @localmode/core

Quick Start

import { IDBStorage } from '@localmode/idb';
import { createVectorDB, embed, ingest } from '@localmode/core';
import { transformers } from '@localmode/transformers';

// Create storage
const storage = new IDBStorage({ name: 'my-app' });

// Use with VectorDB
const db = await createVectorDB({
  name: 'documents',
  dimensions: 384,
  storage,
});

// Ingest documents
const model = transformers.embedding('Xenova/bge-small-en-v1.5');

await ingest({
  db,
  model,
  documents: [
    { text: 'Local-first AI is the future', metadata: { source: 'blog' } },
    { text: 'Privacy-preserving machine learning', metadata: { source: 'paper' } },
  ],
});

The ingest() function is part of the RAG pipeline. It chunks, embeds, and stores documents in a single call.

Configuration

Prop

Type

Why idb?

The idb library is the thinnest possible wrapper around IndexedDB:

  • ~3KB gzipped — Adds almost nothing to your bundle
  • Promise-based — Replaces IndexedDB's awkward event-based API with async/await
  • Typed schemas — Full DBSchema interface for compile-time safety
  • No magic — What you write is what runs; no ORM overhead

When to choose idb

Choose @localmode/idb when bundle size is your top priority and you don't need Dexie's schema versioning or localForage's automatic driver fallback.

Typed Schema

IDBStorage uses idb's DBSchema interface for compile-time type safety on all IndexedDB operations. The schema defines four object stores with typed indexes for efficient collection-scoped lookups:

// Internal schema (handled automatically)
interface VectorDBSchema extends DBSchema {
  documents: {
    key: string;
    value: DocumentRecord;
    indexes: { collectionId: string };  // Fast per-collection queries
  };
  vectors: {
    key: string;
    value: VectorRecord;
    indexes: { collectionId: string };
  };
  indexes: { key: string; value: IndexRecord };
  collections: { key: string; value: CollectionRecord; indexes: { name: string } };
}

This means all get(), put(), and delete() calls are type-checked — you get compile-time errors for invalid keys or values, unlike raw IndexedDB.

Persistence Guarantees

IDBStorage round-trips the full Collection object, so everything core stores on it survives a page reload or database reopen: SQ8/PQ vector quantization calibration (calibration, pqCodebook), storage-compression calibration (compressionCalibration, deltaCalibration, compression), and the embedding-drift modelFingerprint. Quantized and compressed databases decode identically across sessions, and drift detection works the same as with core's built-in IndexedDBStorage.

Used quantization or compression before this fix?

Earlier versions of this adapter persisted only four Collection fields and silently dropped the calibration data — quantized/compressed vectors round-tripped fine in-session but decoded as raw bytes after a reopen. The calibration was never stored, so affected databases cannot be repaired: clear and re-ingest (databases that never used quantization or compression are unaffected).

// Recover a database that used quantization/compression before the fix
await db.clear(); // or delete the underlying database: indexedDB.deleteDatabase('my-app')

// Then re-ingest from your source documents
await ingest({ db, model, documents });

Writing your own adapter? Verify the same guarantees with the StorageAdapter conformance suite — the official adapters run it in their own test suites.

Storage Fallback

Gracefully fallback when IndexedDB is unavailable (e.g., Safari private browsing):

import { MemoryStorage } from '@localmode/core';
import { IDBStorage } from '@localmode/idb';

let storage: IDBStorage | MemoryStorage;

try {
  storage = new IDBStorage({ name: 'my-app' });
  await storage.open();
} catch (error) {
  console.warn('IDBStorage unavailable, falling back to memory:', error);
  storage = new MemoryStorage();
}

Comparison

AdapterPackageBundle SizeTransactionsAuto-FallbackBest For
IndexedDBStorage@localmode/core0KB (built-in)NoNoSimple apps, zero extra deps
DexieStorage@localmode/dexie~15KBYesNoProduction apps needing schema versioning
IDBStorage@localmode/idb~3KBNoNoMinimal bundle size
LocalForageStorage@localmode/localforage~10KBNoYesMax browser compatibility

Next Steps

On this page