Skip to content
Back to skills

Dexie

ASecurity

Dexie.js usage in a local-first Next.js + React + TypeScript app (IndexedDB wrapper, useLiveQuery, schema versioning, migrations)

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 13, 2026
ai-agentstypescriptgobashreactnextjsapidatabase

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned October 13, 2026

npx -y skills add phucbm/skills --skill dexie --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Dexie?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Dexie
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/phucbm-dexie/badge)](https://www.skillsdirectory.com/skills/phucbm-dexie)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: dexie
description: Dexie.js usage in a local-first Next.js + React + TypeScript app (IndexedDB wrapper, useLiveQuery, schema versioning, migrations)
when_to_use: When the user works with Dexie.js — setting up a database, defining schemas, querying, reacting to data changes in React, or migrating existing data
allowed-tools: Read, Edit, Write, Bash, Glob, Grep
---

# Dexie.js — Local-First Patterns

## Why Dexie

- Wraps IndexedDB with a clean Promise/async-await API
- `useLiveQuery()` auto-rerenders React components on data changes (no extra state management)
- Schema versioning with `.upgrade()` handles migrations for existing user data
- Typed tables via `Table<T, PrimaryKey>` — full TypeScript support
- IndexedDB storage cap: gigabytes (vs localStorage ~5 MB)

## Setup

```bash
pnpm add dexie dexie-react-hooks
```

## Database definition (`lib/db.ts`)

```ts
import Dexie, { type Table } from 'dexie';

export interface Item {
  id?: number;
  name: string;
  createdAt: number;
}

class AppDatabase extends Dexie {
  items!: Table<Item, number>;

  constructor() {
    super('AppDatabase');
    this.version(1).stores({
      items: '++id, name',
    });
  }
}

export const db = new AppDatabase();
```

- `++id` — auto-increment primary key
- Only index fields you query/sort by; non-indexed fields are still stored

## Schema migration

```ts
this.version(1).stores({ items: '++id, name' });

this.version(2).stores({ items: '++id, name, category' }).upgrade(tx =>
  tx.table('items').toCollection().modify(item => {
    item.category = item.category ?? 'uncategorized';
  })
);
```

- Always keep old versions — Dexie runs upgrades sequentially for users on older versions
- `.modify()` mutates in place; return value is ignored

## Querying

```ts
const all = await db.items.toArray();
const filtered = await db.items.where('name').equals('foo').toArray();
const sorted = await db.items.orderBy('createdAt').toArray();
const one = await db.items.get(id);
```

## Writes

```ts
const id = await db.items.add({ name: 'Foo', createdAt: Date.now() });
await db.items.update(id, { name: 'Bar' });
await db.items.put({ id: 1, name: 'Bar', createdAt: Date.now() }); // upsert
await db.items.delete(id);
```

## React integration — `useLiveQuery`

```tsx
import { useLiveQuery } from 'dexie-react-hooks';
import { db } from '@/lib/db';

export function ItemList() {
  const items = useLiveQuery(() => db.items.toArray(), []);

  if (!items) return <p>Loading…</p>;
  return <ul>{items.map(i => <li key={i.id}>{i.name}</li>)}</ul>;
}
```

- Returns `undefined` on first render — use as loading state
- Second arg is dependency array — include any variables used inside the query
- Re-renders automatically whenever the queried table changes

## Transactions

```ts
await db.transaction('rw', db.items, async () => {
  await db.items.add({ ... });
  await db.items.update(someId, { ... });
});
```

## Next.js / SSR constraint

- Dexie only runs client-side — use `'use client'` directive or guard with `typeof window !== 'undefined'`
- Never call `db.*` in server components or static data-fetching functions

## Gotchas

- **Index only what you filter/sort on** — over-indexing wastes space and slows writes
- **`useLiveQuery` returns `undefined` initially** — always handle the loading state
- **Compound index**: `'[field1+field2]'` in `.stores()` for multi-field queries
- **Bulk ops**: `db.items.bulkAdd([...])` / `db.items.bulkDelete([...])` for batch writes
- **Version must only increase** — never reuse or decrement a version number

## References

- Docs: https://dexie.org/docs/
- React hooks: https://dexie.org/docs/dexie-react-hooks/useLiveQuery()

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.