dexie vs idb vs localforage
Client-Side Database Storage Strategies
dexieidblocalforageSimilar Packages:

Client-Side Database Storage Strategies

dexie, idb, and localforage are JavaScript libraries designed to simplify client-side data persistence in web applications. dexie is a feature-rich wrapper for IndexedDB that offers a fluent API and schema management. idb is a lightweight, promise-based wrapper that exposes native IndexedDB functionality with minimal abstraction. localforage provides a simple key-value API that works across IndexedDB, WebSQL, and localStorage, abstracting the underlying storage engine completely.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
dexie014,2833.23 MB588a month agoApache-2.0
idb07,32382.8 kB57a year agoISC
localforage025,777-2495 years agoApache-2.0

Dexie vs idb vs localforage: Architecture and API Compared

When building offline-first web apps or caching layers, choosing the right storage library is critical. dexie, idb, and localforage all target client-side persistence, but they solve different problems with different levels of abstraction. Let's compare how they handle schema, queries, and transactions.

πŸ—‚οΈ Schema Definition and Setup

dexie uses a fluent schema definition that feels like an ORM.

  • You define versions and stores explicitly.
  • Indexes are declared in a string format, making it easy to read.
// dexie: Declarative schema
import Dexie from 'dexie';

const db = new Dexie('MyDatabase');
db.version(1).stores({
  friends: '++id, name, age' // id is auto-inc, name and age are indexed
});

idb exposes the native IndexedDB upgrade process via promises.

  • You write the upgrade callback manually.
  • More verbose but gives you exact control over object stores.
// idb: Manual upgrade block
import { openDB } from 'idb';

const db = await openDB('MyDatabase', 1, {
  upgrade(db) {
    const store = db.createObjectStore('friends', {
      keyPath: 'id',
      autoIncrement: true
    });
    store.createIndex('by_age', 'age');
  }
});

localforage has no schema.

  • It is a key-value store, similar to localStorage.
  • You cannot define indexes or object stores explicitly.
// localforage: No schema, just config
import localforage from 'localforage';

localforage.config({
  name: 'MyDatabase',
  version: 1,
  storeName: 'friends' // Acts like a single bucket
});
// Data is stored as key-value pairs only

πŸ” Querying and Filtering Data

dexie allows complex queries using a chainable API.

  • You can filter by ranges, any index, or combine conditions.
  • Returns standard JavaScript arrays.
// dexie: Rich querying
const adults = await db.friends
  .where('age')
  .above(18)
  .and(friend => friend.name.startsWith('A'))
  .toArray();

idb uses native IndexedDB queries via promises.

  • You must use IDBKeyRange for advanced filtering.
  • Powerful but requires knowledge of IndexedDB specifics.
// idb: Native querying
import { IDBKeyRange } from 'idb';

const range = IDBKeyRange.lowerBound(18);
const adults = await db.getAllFromIndex('friends', 'by_age', range);
// Further filtering must be done manually in JS

localforage does not support value-based queries.

  • You can only get items by their key.
  • To find data, you must iterate all keys or maintain your own index.
// localforage: Key-value only
const friend = await localforage.getItem('friend_123');

// To find by value, you must iterate everything (slow)
const keys = await localforage.keys();
// Manual filtering required in application code

πŸ”’ Transaction Management

dexie handles transactions automatically or explicitly.

  • It detects which stores you touch and locks them.
  • Supports atomic operations across multiple stores.
// dexie: Explicit transaction
await db.transaction('rw', db.friends, db.logs, async () => {
  await db.friends.add({ name: 'Alice' });
  await db.logs.add({ action: 'added_friend' });
});

idb requires manual transaction creation.

  • You create a transaction object and commit explicitly.
  • Gives you control over durability modes.
// idb: Manual transaction
const tx = db.transaction('friends', 'readwrite');
await tx.objectStore('friends').add({ name: 'Alice' });
await tx.done; // Waits for completion

localforage does not support multi-key transactions.

  • Each operation is atomic on its own.
  • You cannot guarantee two setItem calls succeed or fail together.
// localforage: No transactions
await localforage.setItem('key1', 'value1');
await localforage.setItem('key2', 'value2');
// If the second fails, the first is already saved

πŸ› οΈ Maintenance and Future Proofing

dexie is actively maintained with a large community.

  • Regular updates for new browser features.
  • Strong TypeScript support out of the box.

idb is maintained by Google engineers (Jake Archibald).

  • Very stable, follows web standards closely.
  • Minimal surface area means fewer breaking changes.

localforage is in maintenance mode with low activity.

  • It was built when IndexedDB support was inconsistent.
  • Modern browsers now support IndexedDB well, reducing the need for its fallbacks.

πŸ“Š Summary: Key Differences

Featuredexieidblocalforage
API Style🟒 Fluent, ORM-like🟑 Promise-based Native🟠 Key-Value (localStorage)
Schemaβœ… Declarative & Versionedβœ… Manual Upgrade Block❌ None
Queryingβœ… Rich (Ranges, Filters)βœ… Native (IDBKeyRange)❌ Keys Only
Transactionsβœ… Multi-store Atomicβœ… Manual Control❌ Per-Operation Only
Bundle Weight🟠 Medium🟒 Tiny🟠 Medium
Status🟒 Active🟒 Active🟑 Maintenance

πŸ’‘ The Big Picture

dexie is the productivity choice 🧰. It removes the pain of IndexedDB while keeping its power. Use it for complex apps like note-takers, caches, or offline sync engines where you need to query data efficiently.

idb is the purist choice πŸ”§. It gives you promises without hiding the engine. Use it if you want standards compliance, minimal dependencies, and you already understand how IndexedDB works.

localforage is the legacy choice πŸ•°οΈ. It is great for dropping into old projects that need better storage than localStorage without refactoring logic. For new projects, prefer dexie or idb to avoid hitting the key-value ceiling later.

Final Thought: IndexedDB is powerful but verbose. dexie makes it enjoyable, idb makes it modern, and localforage makes it simple β€” but too simple for serious data work. Choose based on how much querying you need to do.

How to Choose: dexie vs idb vs localforage

  • dexie:

    Choose dexie if you need complex querying, schema versioning, and a developer-friendly API for IndexedDB. It is ideal for applications that treat the browser database like a real backend, requiring indexes, relationships, and transactional safety without writing boilerplate code.

  • idb:

    Choose idb if you want full control over IndexedDB with modern promise-based syntax but minimal abstraction overhead. It is best for developers who understand IndexedDB concepts and want a tiny, standards-compliant helper that doesn't hide the underlying API mechanics.

  • localforage:

    Choose localforage only for simple key-value storage needs where you want a localStorage-like API but with better performance and capacity. Avoid it for complex queries or new projects requiring advanced IndexedDB features, as it abstracts away the power of IndexedDB and is in maintenance mode.

README for dexie

Dexie.js

NPM Version Build Status Join our Discord

Dexie.js is a wrapper library for indexedDB - the standard database in the browser. https://dexie.org.

Why Dexie.js?

IndexedDB is the portable database for all browser engines. Dexie.js makes it fun and easy to work with.

But also:

  • Dexie.js is widely used by 100,000 of web sites, apps and other projects and supports all browsers, Electron for Desktop apps, Capacitor for iOS / Android apps and of course pure PWAs.
  • Dexie.js works around bugs in the IndexedDB implementations, giving a more stable user experience.
  • Need sync? Dexie Cloud adds real-time sync, auth, and collaboration on top of Dexie.js β€” no backend needed.

Hello World (vanilla JS)

<!DOCTYPE html>
<html>
  <head>
    <script type="module">
      // Import Dexie
      import { Dexie } from 'https://unpkg.com/dexie/dist/modern/dexie.mjs';

      //
      // Declare Database
      //
      const db = new Dexie('FriendDatabase');
      db.version(1).stores({
        friends: '++id, age'
      });

      //
      // Play with it
      //
      try {
        await db.friends.add({ name: 'Alice', age: 21 });

        const youngFriends = await db.friends
            .where('age')
            .below(30)
            .toArray();

        alert(`My young friends: ${JSON.stringify(youngFriends)}`);
      } catch (e) {
        alert(`Oops: ${e}`);
      }
    </script>
  </head>
</html>

Yes, it's that simple. Read the docs to get into the details.

Hello World (legacy script tags)

<!DOCTYPE html>
<html>
  <head>
    <script src="https://unpkg.com/dexie/dist/dexie.js"></script>
    <script>

      //
      // Declare Database
      //
      const db = new Dexie('FriendDatabase');
      db.version(1).stores({
        friends: '++id, age'
      });

      //
      // Play with it
      //
      db.friends.add({ name: 'Alice', age: 21 }).then(() => {
        return db.friends
          .where('age')
          .below(30)
          .toArray();
      }).then(youngFriends => {
        alert (`My young friends: ${JSON.stringify(youngFriends)}`);
      }).catch (e => {
        alert(`Oops: ${e}`);
      });

    </script>
  </head>
</html>

Hello World (React + Typescript)

Real-world apps are often built using components in various frameworks. Here's a version of Hello World written for React and Typescript. There are also links below this sample to more tutorials for different frameworks...

import React from 'react';
import { Dexie, type EntityTable } from 'dexie';
import { useLiveQuery } from 'dexie-react-hooks';

// Typing for your entities (hint is to move this to its own module)
export interface Friend {
  id: number;
  name: string;
  age: number;
}

// Database declaration (move this to its own module also)
export const db = new Dexie('FriendDatabase') as Dexie & {
  friends: EntityTable<Friend, 'id'>;
};
db.version(1).stores({
  friends: '++id, age',
});

// Component:
export function MyDexieReactComponent() {
  const youngFriends = useLiveQuery(() =>
    db.friends
      .where('age')
      .below(30)
      .toArray()
  );

  return (
    <>
      <h3>My young friends</h3>
      <ul>
        {youngFriends?.map((f) => (
          <li key={f.id}>
            Name: {f.name}, Age: {f.age}
          </li>
        ))}
      </ul>
      <button
        onClick={() => {
          db.friends.add({ name: 'Alice', age: 21 });
        }}
      >
        Add another friend
      </button>
    </>
  );
}

Tutorials for React, Svelte, Vue, Angular and vanilla JS

API Reference

Samples

Performance

Dexie has kick-ass performance. Its bulk methods take advantage of a lesser-known feature in IndexedDB that makes it possible to store stuff without listening to every onsuccess event. This speeds up the performance to a maximum.

Supported operations

above(key): Collection;
aboveOrEqual(key): Collection;
add(item, key?): Promise;
and(filter: (x) => boolean): Collection;
anyOf(keys[]): Collection;
anyOfIgnoreCase(keys: string[]): Collection;
below(key): Collection;
belowOrEqual(key): Collection;
between(lower, upper, includeLower?, includeUpper?): Collection;
bulkAdd(items: Array): Promise;
bulkDelete(keys: Array): Promise;
bulkPut(items: Array): Promise;
clear(): Promise;
count(): Promise;
delete(key): Promise;
distinct(): Collection;
each(callback: (obj) => any): Promise;
eachKey(callback: (key) => any): Promise;
eachPrimaryKey(callback: (key) => any): Promise;
eachUniqueKey(callback: (key) => any): Promise;
equals(key): Collection;
equalsIgnoreCase(key): Collection;
filter(fn: (obj) => boolean): Collection;
first(): Promise;
get(key): Promise;
inAnyRange(ranges): Collection;
keys(): Promise;
last(): Promise;
limit(n: number): Collection;
modify(changeCallback: (obj: T, ctx:{value: T}) => void): Promise;
modify(changes: { [keyPath: string]: any } ): Promise;
noneOf(keys: Array): Collection;
notEqual(key): Collection;
offset(n: number): Collection;
or(indexOrPrimayKey: string): WhereClause;
orderBy(index: string): Collection;
primaryKeys(): Promise;
put(item: T, key?: Key): Promise;
reverse(): Collection;
sortBy(keyPath: string): Promise;
startsWith(key: string): Collection;
startsWithAnyOf(prefixes: string[]): Collection;
startsWithAnyOfIgnoreCase(prefixes: string[]): Collection;
startsWithIgnoreCase(key: string): Collection;
toArray(): Promise;
toCollection(): Collection;
uniqueKeys(): Promise;
until(filter: (value) => boolean, includeStopEntry?: boolean): Collection;
update(key: Key, changes: { [keyPath: string]: any }): Promise;

This is a mix of methods from WhereClause, Table and Collection. Dive into the API reference to see the details.

Dexie Cloud

Dexie Cloud is the easiest way to add sync, authentication, and real-time collaboration to your Dexie app. You keep writing frontend code with Dexie.js β€” Dexie Cloud handles the rest.

What you get:

  • πŸ”„ Sync across devices β€” changes propagate in real time, no polling needed
  • πŸ” Authentication β€” built-in user auth, no identity provider required
  • πŸ›‘οΈ Access control β€” share data between users with fine-grained permissions
  • πŸ“ File & blob storage β€” store attachments alongside your structured data
  • ✈️ Offline-first β€” works fully offline, syncs when back online

Getting started is just a few lines:

npm install dexie-cloud-addon
import Dexie from 'dexie';
import dexieCloud from 'dexie-cloud-addon';

const db = new Dexie('MyDatabase', { addons: [dexieCloud] });
db.version(1).stores({ items: '@id, title' });
db.cloud.configure({ databaseUrl: 'https://<your-db>.dexie.cloud' });

That's it. Your existing Dexie app now syncs. Hosted cloud or self-hosted on your own infrastructure. πŸ‘‹

β†’ Quickstart guide

Sample app:

Source: Dexie Cloud To-do app

Live demo: https://dexie.github.io/Dexie.js/dexie-cloud-todo-app/

Samples

https://dexie.org/docs/Samples

https://github.com/dexie/Dexie.js/tree/master/samples

Knowledge Base

https://dexie.org/docs/Questions-and-Answers

Website

https://dexie.org

Install via npm

npm install dexie

Download

For those who don't like package managers, here's the download links:

UMD (for legacy script includes as well as commonjs require):

https://unpkg.com/dexie@latest/dist/dexie.min.js

https://unpkg.com/dexie@latest/dist/dexie.min.js.map

Modern (ES module):

https://unpkg.com/dexie@latest/dist/modern/dexie.min.mjs

https://unpkg.com/dexie@latest/dist/modern/dexie.min.mjs.map

Typings:

https://unpkg.com/dexie@latest/dist/dexie.d.ts

Contributing

See CONTRIBUTING.md

Build

pnpm install
pnpm run build

Test

pnpm test

Watch

pnpm run watch

Browser testing via
TestMu AI (Formerly LambdaTest)