Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ TanStack DB provides several collection types to support different backend integ
- **`@tanstack/query-db-collection`** - Collections backed by [TanStack Query](https://tanstack.com/query) for REST APIs and GraphQL endpoints
- **`@tanstack/electric-db-collection`** - Real-time sync collections powered by [ElectricSQL](https://electric-sql.com) for live database synchronization
- **`@tanstack/trailbase-db-collection`** - Collections for [TrailBase](https://trailbase.io) backend integration
- **`@tanstack/rxdb-db-collection`** - Collections backed by [RxDB](https://rxdb.info), a client-side database with replication support and local persistence made for local-first apps.

## Framework integrations

Expand All @@ -157,7 +158,7 @@ TanStack DB integrates with React & Vue with more on the way!
```bash
npm install @tanstack/react-db
# Optional: for specific collection types
npm install @tanstack/electric-db-collection @tanstack/query-db-collection
npm install @tanstack/electric-db-collection @tanstack/query-db-collection @tanstack/trailbase-db-collection @tanstack/rxdb-db-collection
```

Other framework integrations are in progress.
Expand Down
132 changes: 132 additions & 0 deletions docs/collections/rxdb-collection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
---
title: RxDB Collection
---

# RxDB Collection

RxDB collections provide seamless integration between TanStack DB and [RxDB](https://rxdb.info), enabling automatic synchronization between your in-memory TanStack DB collections and RxDB's local-first database. Giving you offline-ready persistence, and powerful sync capabilities with a wide range of backends.


## Overview

The `@tanstack/rxdb-db-collection` package allows you to create collections that:
- Automatically mirror the state of an underlying RxDB collection
- Reactively update when RxDB documents change
- Support optimistic mutations with rollback on error
- Provide persistence handlers to keep RxDB in sync with TanStack DB transactions
- Sync across browser tabs - changes in one tab are reflected in RxDB and TanStack DB collections in all tabs
- Use one of RxDB's [storage engines](https://rxdb.info/rx-storage.html).
- Work with RxDB's [replication features](https://rxdb.info/replication.html) for offline-first and sync scenarios
- Leverage RxDB's [replication plugins](https://rxdb.info/replication.html) to sync with CouchDB, MongoDB, Supabase, REST APIs, GraphQL, WebRTC (P2P) and more.


## 1. Installation

```bash
npm install @tanstack/rxdb-db-collection rxdb @tanstack/db
```


### 2. Create an RxDatabase and RxCollection

```ts
import { createRxDatabase, addRxPlugin } from 'rxdb/plugins/core'

/**
* Here we use the localstorage based storage for RxDB.
* RxDB has a wide range of storages based on Dexie.js, IndexedDB, SQLite and more.
*/
import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage'

// add json-schema validation (optional)
import { wrappedValidateAjvStorage } from 'rxdb/plugins/validate-ajv';

// Enable dev mode (optional, recommended during development)
import { RxDBDevModePlugin } from 'rxdb/plugins/dev-mode'
addRxPlugin(RxDBDevModePlugin)

type Todo = { id: string; text: string; completed: boolean }

const db = await createRxDatabase({
name: 'my-todos',
storage: wrappedValidateAjvStorage({
storage: getRxStorageLocalstorage()
})
})

await db.addCollections({
todos: {
schema: {
title: 'todos',
version: 0,
type: 'object',
primaryKey: 'id',
properties: {
id: { type: 'string', maxLength: 100 },
text: { type: 'string' },
completed: { type: 'boolean' },
},
required: ['id', 'text', 'completed'],
},
},
})
```


### 3. (optional) sync with a backend
```ts
import { replicateRxCollection } from 'rxdb/plugins/replication'
const replicationState = replicateRxCollection({
collection: db.todos,
pull: { handler: myPullHandler },
push: { handler: myPushHandler },
})
```

### 4. Wrap the RxDB collection with TanStack DB

```ts
import { createCollection } from '@tanstack/db'
import { rxdbCollectionOptions } from '@tanstack/rxdb-db-collection'

const todosCollection = createCollection(
rxdbCollectionOptions({
rxCollection: myDatabase.todos,
startSync: true, // start ingesting RxDB data immediately
})
)
```


Now `todosCollection` is a reactive TanStack DB collection driven by RxDB:

- Writes via `todosCollection.insert/update/delete` persist to RxDB.
- Direct writes in RxDB (or via replication) flow into the TanStack collection via change streams.



## Configuration Options

The `rxdbCollectionOptions` function accepts the following options:

### Required

- `rxCollection`: The underlying [RxDB collection](https://rxdb.info/rx-collection.html)

### Optional

- `id`: Unique identifier for the collection
- `schema`: Schema for validating items. RxDB already has schema validation but having additional validation on the TanStack DB side can help to unify error handling between different tanstack collections.
- `startSync`: Whether to start syncing immediately (default: true)
- `onInsert, onUpdate, onDelete`: Override default persistence handlers. By default, TanStack DB writes are persisted to RxDB using bulkUpsert, patch, and bulkRemove.
- `syncBatchSize`: The maximum number of documents fetched per batch during the initial sync from RxDB into TanStack DB (default: 1000). Larger values reduce round trips but use more memory; smaller values are lighter but may increase query calls. Note that this only affects the initial sync. Ongoing live updates are streamed one by one via RxDB's change feed.



## Syncing with Backends

Replication and sync in RxDB run independently of TanStack DB. You set up replication directly on your RxCollection using RxDB's replication plugins (for CouchDB, GraphQL, WebRTC, REST APIs, etc.).

When replication runs, it pulls and pushes changes to the backend and applies them to the RxDB collection. Since the TanStack DB integration subscribes to the RxDB change stream, any changes applied by replication are automatically reflected in your TanStack DB collection.

This separation of concerns means you configure replication entirely in RxDB, and TanStack DB automatically benefits: your TanStack collections always stay up to date with whatever sync strategy you choose.
12 changes: 12 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@
{
"label": "Electric Collection",
"to": "collections/electric-collection"
},
{
"label": "RxDB Collection",
"to": "collections/rxdb-collection"
}
]
},
Expand Down Expand Up @@ -137,6 +141,14 @@
{
"label": "queryCollectionOptions",
"to": "reference/query-db-collection/functions/querycollectionoptions"
},
{
"label": "RxDB DB Collection",
"to": "reference/rxdb-db-collection/index"
},
{
"label": "rxdbCollectionOptions",
"to": "reference/rxdb-db-collection/functions/rxdbcollectionoptions"
}
],
"frameworks": [
Expand Down
8 changes: 7 additions & 1 deletion docs/guides/collection-options-creator.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Collection options creators follow a consistent pattern:
## When to Create a Custom Collection

You should create a custom collection when:
- You have a dedicated sync engine (like ElectricSQL, Trailbase, Firebase, or a custom WebSocket solution)
- You have a dedicated sync engine (like ElectricSQL, Trailbase, Firebase, RxDB or a custom WebSocket solution)
- You need specific sync behaviors that aren't covered by the query collection
- You want to integrate with a backend that has its own sync protocol

Expand Down Expand Up @@ -331,6 +331,7 @@ For complete, production-ready examples, see the collection packages in the TanS
- **[@tanstack/query-collection](https://github.com/TanStack/db/tree/main/packages/query-collection)** - Pattern A: User-provided handlers with full refetch strategy
- **[@tanstack/trailbase-collection](https://github.com/TanStack/db/tree/main/packages/trailbase-collection)** - Pattern B: Built-in handlers with ID-based tracking
- **[@tanstack/electric-collection](https://github.com/TanStack/db/tree/main/packages/electric-collection)** - Pattern A: Transaction ID tracking with complex sync protocols
- **[@tanstack/rxdb-collection](https://github.com/TanStack/db/tree/main/packages/rxdb-collection)** - Pattern B: Built-in handlers that bridge [RxDB](https://rxdb.info) change streams into TanStack DB's sync lifecycle

### Key Lessons from Production Collections

Expand All @@ -349,6 +350,11 @@ For complete, production-ready examples, see the collection packages in the TanS
- Demonstrates advanced deduplication techniques
- Shows how to wrap user handlers with sync coordination

**From RxDB Collection:**
- Uses RxDB's built-in queries and change streams
- Uses `RxCollection.$` to subscribe to inserts/updates/deletes and forward them to TanStack DB with begin-write-commit
- Implements built-in mutation handlers (onInsert, onUpdate, onDelete) that call RxDB APIs (bulkUpsert, incrementalPatch, bulkRemove)

## Complete Example: WebSocket Collection

Here's a complete example of a WebSocket-based collection options creator that demonstrates the full round-trip flow:
Expand Down
11 changes: 11 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,14 @@ npm install @tanstack/trailbase-db-collection
```

Use `trailBaseCollectionOptions` to sync records from TrailBase's Record APIs with built-in subscription support.

### RxDB Collection

For offline-first apps and local persistence with [RxDB](https://rxdb.info):

```sh
npm install @tanstack/rxdb-db-collection
```

Use `rxdbCollectionOptions` to bridge an [RxDB collection](https://rxdb.info/rx-collection.html) into TanStack DB.
This gives you reactive TanStack DB collections backed by RxDB's powerful local-first database, replication, and conflict handling features.
51 changes: 49 additions & 2 deletions docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,9 @@ There are a number of built-in collection types:
1. [`QueryCollection`](#querycollection) to load data into collections using [TanStack Query](https://tanstack.com/query)
2. [`ElectricCollection`](#electriccollection) to sync data into collections using [ElectricSQL](https://electric-sql.com)
3. [`TrailBaseCollection`](#trailbasecollection) to sync data into collections using [TrailBase](https://trailbase.io)
4. [`LocalStorageCollection`](#localstoragecollection) for small amounts of local-only state that syncs across browser tabs
5. [`LocalOnlyCollection`](#localonlycollection) for in-memory client data or UI state
4. [`RxDBCollection`](#rxdbcollection) to integrate with [RxDB](https://rxdb.info) for local persistence and sync
5. [`LocalStorageCollection`](#localstoragecollection) for small amounts of local-only state that syncs across browser tabs
6. [`LocalOnlyCollection`](#localonlycollection) for in-memory client data or UI state

You can also use:

Expand Down Expand Up @@ -301,6 +302,52 @@ This collection requires the following TrailBase-specific options:
A new collections doesn't start syncing until you call `collection.preload()` or you query it.


#### `RxDBCollection`

[RxDB](https://rxdb.info) is a client-side database for JavaScript apps with replication, conflict resolution, and offline-first features.
Use `rxdbCollectionOptions` from `@tanstack/rxdb-db-collection` to integrate an RxDB collection with TanStack DB:

```ts
import { createCollection } from "@tanstack/react-db"
import { rxdbCollectionOptions } from "@tanstack/rxdb-db-collection"
import { createRxDatabase } from "rxdb"

const db = await createRxDatabase({
name: "mydb",
storage: getRxStorageMemory(),
})
await db.addCollections({
todos: {
schema: {
version: 0,
primaryKey: "id",
type: "object",
properties: {
id: { type: "string", maxLength: 100 },
text: { type: "string" },
completed: { type: "boolean" },
},
},
},
})

// Wrap the RxDB collection with TanStack DB
export const todoCollection = createCollection(
rxdbCollectionOptions({
rxCollection: db.todos,
startSync: true
})
)
```

With this integration:

- TanStack DB subscribes to RxDB's change streams and reflects updates, deletes, and inserts in real-time.
- You get local-first sync when RxDB replication is configured.
- Mutation handlers (onInsert, onUpdate, onDelete) are implemented using RxDB's APIs (bulkUpsert, incrementalPatch, bulkRemove).

This makes RxDB a great choice for apps that need local-first storage, replication, or peer-to-peer sync combined with TanStack DB's live queries and transaction lifecycle.

#### `LocalStorageCollection`

localStorage collections store small amounts of local-only state that persists across browser sessions and syncs across browser tabs in real-time. All data is stored under a single localStorage key and automatically synchronized using storage events.
Expand Down
5 changes: 5 additions & 0 deletions packages/rxdb-db-collection/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# @tanstack/rxdb-db-collection

## 0.0.0

- Initial Release
71 changes: 71 additions & 0 deletions packages/rxdb-db-collection/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"name": "@tanstack/rxdb-db-collection",
"description": "RxDB collection for TanStack DB",
"version": "0.1.4",
"dependencies": {
"rxdb": "16.17.2",
"@standard-schema/spec": "^1.0.0",
"@tanstack/db": "workspace:*",
"@tanstack/store": "^0.7.0",
"debug": "^4.4.1"
},
"devDependencies": {
"@types/debug": "^4.1.12",
"@vitest/coverage-istanbul": "^3.0.9"
},
"exports": {
".": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/cjs/index.d.cts",
"default": "./dist/cjs/index.cjs"
}
},
"./package.json": "./package.json"
},
"files": [
"dist",
"src"
],
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.js",
"packageManager": "[email protected]",
"peerDependencies": {
"rxdb": ">=16.17.2",
"typescript": ">=4.7"
},
"author": "Kyle Mathews",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/TanStack/db.git",
"directory": "packages/rxdb-db-collection"
},
"homepage": "https://tanstack.com/db",
"keywords": [
"rxdb",
"nosql",
"realtime",
"local-first",
"sync-engine",
"sync",
"replication",
"opfs",
"indexeddb",
"localstorage",
"optimistic",
"typescript"
],
"scripts": {
"build": "vite build",
"dev": "vite build --watch",
"lint": "eslint . --fix",
"test": "npx vitest --run"
},
"sideEffects": false,
"type": "module",
"types": "dist/esm/index.d.ts"
}
16 changes: 16 additions & 0 deletions packages/rxdb-db-collection/src/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const RESERVED_RXDB_FIELDS = new Set([
'_rev',
'_deleted',
'_attachments',
'_meta',
])

export function stripRxdbFields<T extends Record<string, any>>(obj: T): T {
if (!obj) return obj
const out: any = Array.isArray(obj) ? [] : {}
for (const k of Object.keys(obj)) {
if (RESERVED_RXDB_FIELDS.has(k)) continue
out[k] = obj[k]
}
return out as T
}
2 changes: 2 additions & 0 deletions packages/rxdb-db-collection/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./rxdb"
export * from "./helper"
Loading
Loading