# PGlite: Postgres compiled to WASM, and what its single-connection design costs you

> PGlite packages a WASM build of Postgres into a TypeScript library that runs in the browser, Node.js, Bun and Deno. It is a real Postgres, but it is single-user, and that shapes every decision around it.

**electric-sql/pglite** — Embeddable Postgres with real-time, reactive bindings.

- Repository: https://github.com/electric-sql/pglite
- Website: https://pglite.dev
- Stars: 16,099 · Forks: 447
- Language: TypeScript
- License: Apache-2.0
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/electric-sql-pglite

## The problem PGlite solves: a full Postgres engine with no server to run

Most embedded databases force a choice. You can have SQLite, which is small and fast but speaks its own dialect, or you can have Postgres, which means a server process, a port, credentials and a deployment. PGlite refuses that choice. It is the WASM build of Postgres from Electric, packaged into a TypeScript client library, and the README states it runs in the browser, Node.js, Bun and Deno with no need to install any other dependencies.

The audience is narrower than "anyone who wants a database". It is developers building local-first apps that need Postgres semantics in the client, teams that want a real Postgres in browser-based tests, and anyone prototyping against Postgres features (extensions, types, query planner behaviour) without standing up infrastructure. The README lists support for many Postgres extensions, including pgvector and PostGIS. That matters: if you are testing a vector search query, you want the actual pgvector extension, not a reimplementation of it.

The size claim in the README is 3mb gzipped. That is the entire engine, not a client that talks to one. For a browser application, that is a real download cost you should weigh against the alternative of an API call to a hosted database.

## Why Postgres could be compiled to WASM at all

This is the part worth understanding, because it explains the limitation that follows. PostgreSQL normally uses a process forking model: every client connection forks a new process. Emscripten, the C to WebAssembly compiler, cannot fork new processes and operates strictly in single-process mode. So a direct compile of Postgres to WASM does not work.

Postgres happens to ship a "single user mode", intended for command-line use during bootstrapping and recovery. PGlite builds on that mode and adds an input/output pathway so the engine can be driven from JavaScript when compiled to WASM. The README credits Stas Kelvich of Neon for the earlier work this builds on.

The architectural consequence is stated plainly in the README's limitations section: PGlite is single user/connection. This is not a bug waiting to be fixed by a configuration flag. It falls out of the compilation model. Any design that assumes concurrent connections, connection pooling, or multiple writers hitting the same database must be reconsidered before you adopt PGlite, not after.

## Installing PGlite and running your first query

Installation differs by runtime. The README gives a separate command for each of the three server-side runtimes it supports.

For Node.js:

```bash
npm install @electric-sql/pglite
```

For Bun:

```bash
bun install @electric-sql/pglite
```

For Deno:

```bash
deno add npm:@electric-sql/pglite
```

In the browser, the README says you can import from your usual package manager or from a CDN such as JSDeliver. The CDN form is a single import with no build step:

```js
import { PGlite } from "https://cdn.jsdelivr.net/npm/@electric-sql/pglite/dist/index.js";
```

With the import in place, an in-memory database and a query look like this. The README shows the result shape, which is an object with a rows array:

```javascript
import { PGlite } from "@electric-sql/pglite";

const db = new PGlite();
await db.query("select 'Hello world' as message;");
// -> { rows: [ { message: "Hello world" } ] }
```

Nothing is persisted in that example. To keep data, the constructor takes a path, and where that path points depends on the runtime. In the browser the README uses an indexedDB URL:

```js
const db = new PGlite("idb://my-pgdata");
```

In Node, Bun or Deno, the README passes a filesystem path instead:

```javascript
const db = new PGlite("./path/to/pgdata");
```

The choice between `idb://` and a filesystem path is the first real decision you make, and it is not reversible by changing a config value. The storage backend is fixed by the argument you pass at construction.

## The single-connection limit and where PGlite is the wrong tool

The README's limitations section has exactly one bullet, and it is the one that decides most adoption questions: PGlite is single user/connection. Everything below follows from that sentence.

If your application has multiple concurrent clients writing to shared data, PGlite is the wrong tool. There is no connection pool to tune, no replication slot to configure, no read replica to add. A hosted Postgres, or Postgres in a container, is the correct answer, and the migration is not a drop-in: your code stops talking to a local engine and starts talking to a network endpoint with credentials and latency.

If you need the database to survive a browser tab closing without a sync layer, PGlite on its own will not do that. IndexedDB persistence keeps data in that browser profile on that device. The README describes PGlite as a way to build local-first apps, and local-first implies a sync mechanism that PGlite itself does not provide; the README points to pglite.dev for full documentation and user guides rather than describing sync in the repository README.

There is also a status signal in the README badges: the project labels itself alpha. Treat that as a statement about API stability and production readiness, not as modesty. If you are evaluating PGlite for a production system, the alpha badge and the single-connection limit are the two facts to resolve before anything else.

## How PGlite differs from SQLite-based options

The most common comparison is with SQLite, including its own WASM builds. The difference is not performance, it is dialect and feature surface. SQLite is a separate database with its own type system, its own query planner and its own extension model. Code written against SQLite does not run against Postgres unchanged, and vice versa.

PGlite is Postgres. That means the SQL you write against PGlite is the SQL you deploy against a Postgres server, and the extensions are the real ones. The README names pgvector and PostGIS specifically. If your application depends on Postgres-specific behaviour, a SQLite WASM build cannot substitute for it, because the thing you are testing is Postgres itself.

The trade-off runs the other way too. SQLite's WASM builds are a mature, widely deployed target with a long history of running in browsers. PGlite is younger and carries the alpha badge. It also carries a size cost: the README states 3mb gzipped for the engine. If your use case is a small amount of structured local storage and you have no Postgres dependency, the smaller SQLite option is the more conservative choice, and choosing PGlite there buys you compatibility you do not need.

## Licence, packages and what a PGlite upgrade actually involves

PGlite is Apache-2.0. The repository also carries a separate POSTGRES-LICENSE file at the top level, which is expected: the project embeds PostgreSQL, and PostgreSQL's own licence is not Apache-2.0. If you redistribute PGlite inside a product, read both files. This is not legal advice, and the two-licence layout is the kind of thing worth a look from whoever handles compliance on your team rather than an assumption either way.

The repository is a pnpm workspace. The root package.json sets engines to node >=20 and pnpm >=9, and declares packageManager pnpm@9.7.0. The packages/ directory holds the client library plus a set of extension packages, and the root scripts show the pattern: wasm:copy-pgvector, wasm:copy-postgis, wasm:copy-pg_uuidv7, wasm:copy-pgtap, wasm:copy-pg_ivm, wasm:copy-pg_hashids, wasm:copy-age, wasm:copy-pg_textsearch, wasm:copy-pgmq. Each copies a tarball out of postgres-pglite/dist/extensions/other into that package's release directory.

That layout is the practical upgrade story. Extension support is versioned separately from the core client, which is why the recent releases list shows @electric-sql/pglite@0.5.8 and @electric-sql/pglite-vue@0.4.8 as distinct version lines. If you depend on an extension, you are tracking two version numbers, not one, and an upgrade of the core client does not automatically move the extension package with it.

Building from source is heavier than installing. The README states Docker is required to build the WASM module, and that the build splits into two parts: the Postgres WASM module, and the TypeScript packages. `pnpm build:all` runs both. If you only want the TypeScript side, `pnpm ts:build` builds all packages in dependency order. The README also notes that prebuilt WASM binaries are generated automatically on GitHub after each successful PR merge, available via the link after the comment "Interim build files:" on the last merged PR, to be extracted under packages/pglite/release. That is the path to take if you do not want to run the Docker build.

## Who should adopt PGlite, and who should not

Adopt PGlite if you are building a local-first application that already depends on Postgres semantics, if you want a real Postgres engine inside browser-based tests, or if you are prototyping against pgvector, PostGIS or another extension that PGlite packages. The install is one package manager command, the first query is four lines, and the result shape is a plain object with a rows array. There is no server to start and no port to open.

Do not adopt it as a replacement for a Postgres server serving concurrent clients. The README's single limitation bullet rules that out, and no configuration changes it. Do not adopt it for a multi-device application without deciding separately how data moves between devices, because the README does not describe a sync mechanism; it directs you to pglite.dev for the full documentation.

Before you commit, verify three things against the version you intend to pin. First, that the extensions you need exist as packages under packages/, since extension support ships on its own version line. Second, that your runtime meets engines: node >=20 and pnpm >=9 if you are building from the repository. Third, that you have read the alpha status in the README and the release notes for the specific version, because the project's own badge is the clearest statement about where it stands.

## Conclusion

Adopt PGlite when you need a real Postgres engine inside a browser tab, a test suite or a local-first app, and when one connection at a time is enough. Do not adopt it as a server replacement for concurrent clients, and do not assume the alpha status badge is decoration. Before committing, verify that the extensions you depend on have a matching package under packages/, and check the release notes for the version you pin.

## FAQ

### What is PGlite?

PGlite is a WASM build of Postgres packaged into a TypeScript client library. The README states it runs in the browser, Node.js, Bun and Deno with no need to install any other dependencies, and is 3mb gzipped.

### How do I install PGlite?

Install the @electric-sql/pglite package with your runtime's package manager: npm install @electric-sql/pglite for Node.js, bun install @electric-sql/pglite for Bun, or deno add npm:@electric-sql/pglite for Deno. In the browser the README also shows importing directly from the JSDeliver CDN.

### Is PGlite production ready?

The README carries an alpha status badge, and its limitations section states that PGlite is single user/connection. Those are the two facts the repository itself gives on readiness.

### How do I use PGlite?

Import PGlite from @electric-sql/pglite, construct it with new PGlite(), and call db.query() with a SQL string. The README shows the result arriving as an object with a rows array, and passing a path or an idb:// URL to the constructor makes the database persist instead of staying in memory.

### Is there a PostgreSQL Lite?

PGlite is the closest thing the material describes: a WASM build of Postgres packaged as a TypeScript library, not a separate lightweight reimplementation. The README states it is simply Postgres in WASM and does not use a Linux virtual machine.

## Sources

- [electric-sql/pglite on GitHub](https://github.com/electric-sql/pglite)
- [License: Apache-2.0](https://github.com/electric-sql/pglite/blob/main/LICENSE)
- [Project website](https://pglite.dev)
- [README](https://github.com/electric-sql/pglite/blob/main/README.md)
- [Releases](https://github.com/electric-sql/pglite/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/electric-sql-pglite
