docs-src/docs/articles/tanstack-db/tanstack-db-pglite.md
import {Steps} from '@site/src/components/steps';
PGlite is a WASM build of Postgres that runs in the browser, and a common idea is to use it as the durable storage layer under TanStack DB. TanStack DB is an in-memory reactive client store with live queries and optimistic mutations, and it delegates persistence and sync to the collection type you choose. There is no official PGlite collection for TanStack DB today. RxDB on the other hand has an official, maintained integration through the @tanstack/rxdb-db-collection package, described in TanStack DB + RxDB. This page explains what PGlite is, what wiring it under TanStack DB would take, when PGlite is the right tool, and when an RxDB-backed collection fits better.
PGlite is a WASM build of Postgres from Electric, packaged as a TypeScript client library. It runs Postgres in the browser, Node.js, Bun, and Deno without any external dependencies, and the whole build is about 3 MB gzipped. Unlike earlier "Postgres in the browser" projects it does not boot a Linux virtual machine. It is plain Postgres compiled to WebAssembly, running in the single-user mode that Postgres ships for bootstrapping and recovery.
The facts that matter for a client-side storage decision:
pgvector for vector search and PostGIS for geospatial data.idb://my-database data directory, or to the Origin Private File System (OPFS) through its access-handle-pool filesystem, which only works inside a Web Worker.@electric-sql/pglite/live extension adds live.query(), live.incrementalQuery(), and live.changes(), so you can subscribe to a SQL query and receive updated results when the underlying tables change.This is what PGlite looks like standalone. Notice that this snippet is plain PGlite and has no TanStack DB integration:
// Standalone PGlite, NOT wired into TanStack DB.
import { PGlite } from '@electric-sql/pglite';
// 'idb://...' persists the data directory to IndexedDB.
const pg = new PGlite('idb://my-pgdata');
const result = await pg.query("select 'Hello world' as message;");
// > { rows: [ { message: "Hello world" } ] }
TanStack DB ships official collection types for TanStack Query, Electric, TrailBase, RxDB, PowerSync, localStorage, and local-only data. PGlite is not on that list. There is no @tanstack/pglite-db-collection package, so when you want PGlite under TanStack DB, you have to write the persistence glue yourself.
Conceptually that glue has three parts:
SELECT against PGlite and insert the rows into the TanStack DB collection so the in-memory state matches the database.Each part is doable, but together they form a small sync protocol between two stores with different data models: TanStack DB thinks in documents and keys, Postgres thinks in rows, columns, and schemas. You also own the edge cases: mapping rows to objects and back, transactional ordering of mutations, avoiding echo loops where your own write comes back through the change feed, and coordinating the single PGlite connection across multiple tabs. None of this is impossible. It is just custom infrastructure code that you have to write, test, and maintain, and this page will not pretend otherwise by showing an invented adapter.
For completeness: TanStack also ships its own SQLite persistence packages (@tanstack/db-sqlite-persistence-core with adapters for browser, Node.js, Electron, Expo, React Native, and Capacitor). When you only need SQL-flavored durability for TanStack DB and not Postgres itself, those are worth a look, and the SQLite guide compares them with the RxDB approach.
PGlite shines when the point of your app is Postgres, not just persistence:
pgvector similarity search, or PostGIS queries in the browser.In these cases the missing TanStack DB adapter may be worth the custom glue code, or you skip TanStack DB and build directly on PGlite's own live queries.
When your goal is a durable, syncable TanStack DB collection rather than Postgres itself, RxDB is the shorter path. The trouble with the do-it-yourself PGlite wiring is that you rebuild what already exists as a maintained package:
@tanstack/rxdb-db-collection is an official TanStack package. Initial load, write path, rollback on error, and the change feed are already implemented and tested. The setup is a single rxdbCollectionOptions({ rxCollection }) call.There is also a build-size argument. PGlite adds about 3 MB gzipped of WASM before your app code. That is impressive for a full Postgres, but it is a lot of payload when all you need is durable document storage under an in-memory store.
To be clear about the reverse direction: there is no RxDB storage based on PGlite either. RxDB is a NoSQL document database and stores its data in storages like IndexedDB, OPFS, or SQLite, not in a client-side Postgres.
The following is the working alternative to the hypothetical PGlite adapter: a TanStack DB collection persisted through RxDB. It uses the free localStorage-based storage, and any other RxStorage works the same way. The full setup with replication is in the hub article.
<Steps>npm install rxdb rxjs @tanstack/react-db @tanstack/rxdb-db-collection
import { createRxDatabase } from 'rxdb/plugins/core';
import { getRxStorageLocalstorage } from 'rxdb/plugins/storage-localstorage';
const db = await createRxDatabase({
name: 'notesdb',
storage: getRxStorageLocalstorage()
});
await db.addCollections({
notes: {
schema: {
title: 'notes',
version: 0,
type: 'object',
primaryKey: 'id',
properties: {
id: { type: 'string', maxLength: 100 },
text: { type: 'string' },
archived: { type: 'boolean' }
},
required: ['id', 'text', 'archived']
}
}
});
import { createCollection } from '@tanstack/react-db';
import { rxdbCollectionOptions } from '@tanstack/rxdb-db-collection';
const notesCollection = createCollection(
rxdbCollectionOptions({
rxCollection: db.notes
})
);
The collection loads its initial state from disk and stays in sync with RxDB from then on. Changes written by replication, other tabs, or direct RxDB code stream into it automatically.
import { useLiveQuery, eq } from '@tanstack/react-db';
function NoteList() {
// Live query: re-renders whenever a matching document changes.
const { data: activeNotes } = useLiveQuery((q) =>
q
.from({ note: notesCollection })
.where(({ note }) => eq(note.archived, false))
);
return (
<ul>
{activeNotes.map((note) => (
<li key={note.id}>{note.text}</li>
))}
</ul>
);
}
// Writes are optimistic in memory and persisted to RxDB.
notesCollection.insert({
id: 'note-1',
text: 'compare PGlite and RxDB',
archived: false
});
notesCollection.update('note-1', (draft) => {
draft.archived = true;
});
This is done. No custom adapter, no SQL-to-document mapping, and the data survives a reload.
No. The official TanStack DB collection types cover TanStack Query, Electric, TrailBase, RxDB, PowerSync, localStorage, and local-only data. For PGlite you would have to write your own persistence glue, while the RxDB collection is an official, maintained package.
</details> <details> <summary>Can PGlite persist data in the browser?</summary>Yes. PGlite defaults to an in-memory database, and in the browser it can persist its data directory to IndexedDB via an idb:// data directory or to OPFS through its access-handle-pool filesystem inside a Web Worker. This persists the Postgres files themselves, which is different from a document store like RxDB that persists JSON documents through an RxStorage.
No. There is no RxDB storage built on PGlite or any other client-side Postgres. RxDB is a NoSQL document database and ships storages for localStorage, IndexedDB, OPFS, SQLite, and more. When you need Postgres semantics on the client, use PGlite directly instead of forcing it under a document database.
</details> <details> <summary>Does the RxDB-backed TanStack DB collection sync with a Postgres backend?</summary>Yes. Replication is configured on the RxDB collection through the Sync Engine, which works with any backend, including a server-side PostgreSQL exposed over HTTP or GraphQL. Pulled documents stream into the TanStack DB collection automatically, so Postgres stays on the server where it scales best.
</details>