docs-src/docs/articles/indexeddb/indexeddb-sync.md
import {Faq, FaqItem} from '@site/src/components/faq';
IndexedDB stores structured data inside a single browser, on a single device, in a single origin. That is the whole design. It has no concept of syncing that data to another tab, another device, or a backend server. As soon as your app needs the same data in more than one place, you have to build IndexedDB sync yourself, or use a library that ships it.
This page explains what IndexedDB sync means, why the native API gives you nothing for it, the different levels of sync you might need, and how RxDB adds realtime replication on top of IndexedDB.
<RxdbLogo alt="IndexedDB Sync" />"Sync" is used for three different problems, and it helps to keep them apart:
Native IndexedDB solves none of these. It only stores and reads data in one place.
IndexedDB was designed as a low-level storage building block, not a database engine. It gives you object stores, indexes, and transactions. It does not give you:
So syncing IndexedDB is not a small helper on top of the API. You end up rebuilding change feeds, a revision system, and a replication protocol. That is a database.
The first level of sync happens inside one browser. When a user opens your app in two tabs, both tabs read and write the same IndexedDB database, and a write in one tab should update the UI in the other.
The browser primitive for this is the BroadcastChannel API, which sends messages between tabs of the same origin. You can send a message on every write and have other tabs re-read the changed data. Doing this by hand is error-prone, because you also have to avoid running the same background work in every tab at once.
RxDB handles this out of the box. With multiInstance: true, writes in one tab are visible to reactive queries in every other tab, change events propagate over a BroadcastChannel, and leader election picks a single tab to run the server replication so you do not open one connection per tab.
import { createRxDatabase } from 'rxdb/plugins/core';
import { getRxStorageIndexedDB } from 'rxdb-premium/plugins/storage-indexeddb';
const db = await createRxDatabase({
name: 'mydb',
storage: getRxStorageIndexedDB(),
// Coordinate the same database across all tabs of this origin.
multiInstance: true
});
The second level is syncing the browser's IndexedDB copy with a backend. This is what most people mean by "IndexedDB sync". The client keeps working on the local database, and a replication process moves changes to and from the server.
To do this correctly you need three things that raw IndexedDB lacks:
_meta field and revision on every document for exactly this.RxDB packages this into its Sync Engine. The backend does not have to run RxDB. You can replicate against any infrastructure through the general replication protocol or one of the ready-made plugins:
import { replicateRxCollection } from 'rxdb/plugins/replication';
const replicationState = replicateRxCollection({
collection: db.todos,
replicationIdentifier: 'my-todos-http-replication',
pull: {
async handler(checkpointOrNull, batchSize) {
// Ask the server for documents changed since the last checkpoint.
const response = await fetch(`/api/pull?since=${/* checkpoint */ ''}`);
const data = await response.json();
return { documents: data.documents, checkpoint: data.checkpoint };
}
},
push: {
async handler(changeRows) {
// Send local writes to the server and return conflicts, if any.
const response = await fetch('/api/push', {
method: 'POST',
body: JSON.stringify(changeRows)
});
return response.json();
}
}
});
Because the replication runs on top of the local database, reads and writes stay zero-latency. The user never waits for the network. The sync happens in the background and continues where it left off after the client goes offline and back online.
The third level skips the central server. Clients exchange changes directly with each other. RxDB supports this with WebRTC replication, where peers connect through a signaling server and then sync documents directly. This suits collaborative apps where a backend is optional or where devices on the same network should sync without the cloud.
There are a few ways to get sync onto IndexedDB. They differ in how much they hand you.
| Sync capability | Raw IndexedDB | Dexie.js | PouchDB | RxDB |
|---|---|---|---|---|
| Multi-tab change events | ❌ | ⚠️ manual | ⚠️ manual | ✅ built in |
| Leader election across tabs | ❌ | ❌ | ❌ | ✅ built in |
| Client-server replication | ❌ | ⚠️ paid add-on | ✅ CouchDB only | ✅ many backends |
| Offline then catch up | ❌ | ❌ | ✅ | ✅ |
| Change tracking / checkpoints | ❌ | ❌ | ✅ | ✅ |
| Conflict handling | ❌ | ❌ | ✅ revision tree | ✅ revisions + custom handler |
| Peer-to-peer sync | ❌ | ❌ | ⚠️ via CouchDB | ✅ WebRTC |
| Backend requirement | none | none | CouchDB | any (GraphQL, HTTP, more) |
No. IndexedDB is scoped to one browser on one device and has no network layer. To sync across devices you need a replication process that moves changes through a server or a peer connection. RxDB provides this with its Sync Engine.
</FaqItem> <FaqItem question="How do I sync IndexedDB between browser tabs?">Use the BroadcastChannel API to notify other tabs of writes, or let a database handle it. With RxDB and multiInstance: true, writes in one tab reach reactive queries in every other tab automatically, and leader election keeps a single tab responsible for the server connection.
Yes, when the database tracks changes. The client reads and writes to the local IndexedDB copy while offline, and the replication sends the queued changes once the connection returns. This is the core of the offline-first approach that RxDB is built for.
</FaqItem> <FaqItem question="What happens on a conflict when two devices edit the same document?">The sync layer needs per-document revisions to detect that both sides changed. RxDB attaches a revision to every document and runs a conflict handler that you can customize, so you decide whether the local write, the remote write, or a merge wins.
</FaqItem> <FaqItem question="Do I need a special backend for IndexedDB sync?">No. RxDB replicates against any infrastructure. There are plugins for GraphQL, plain HTTP, CouchDB, Firestore, Supabase, and others, and you can implement the replication protocol against your own server.
</FaqItem> </Faq>