What is ElectricSQL
ElectricSQL is an open-source platform that synchronizes PostgreSQL with client applications in real-time, creating a local-first experience without sacrificing data consistency. Unlike traditional client-server approaches where the database is a "remote source of truth", ElectricSQL allows clients to have a complete local copy of the data they care about, operate offline without network dependency, and synchronize automatically with the server when connection is available. It's built on PostgreSQL (not replacing it), adding a sync layer that handles conflicts, causal ordering, and eventual consistency across all replicas.
💡 Core features
- Real-time sync: Bidirectional push and pull between client and server
- Offline capability: Works without connection, auto-syncs when online
- Conflict resolution: Automatic with CRDTs and causal consistency
- Native PostgreSQL: Use the database you already know, no exotic new tech
- Schema-driven: Define with standard SQL, rest is automatic
Synchronization architecture
ElectricSQL is divided into three key components: the sync server (Electric Server), the JavaScript/TypeScript client (ElectricClient), and the PostgreSQL database. The sync flow works like this: when the client modifies a record locally, ElectricClient records it in a sync queue. Periodically (or immediately if network is available), the queue is sent to Electric Server. The server applies changes to PostgreSQL, checks for conflicts, and reflects the result back to all interested clients. Clients receive updates and apply them to the local database (SQLite or OPFS) in ACID transaction.
// PostgreSQL schema (source of truth)
CREATE TABLE items (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title TEXT NOT NULL,
completed BOOLEAN DEFAULT false,
userId UUID NOT NULL REFERENCES users(id),
createdAt TIMESTAMPTZ DEFAULT now(),
updatedAt TIMESTAMPTZ DEFAULT now()
);
// Client TypeScript - Define local schema
import {{ '{' }} ElectricDatabase {{ '}' }} from 'electric-sql';
const db = new ElectricDatabase('items_db');
// Sync only current user's records
const {{ '{' }} items {{ '}' }} = await db.syncTable('items', {{ '{' }}
where: `userId = '${{ '{' }}currentUserId{{ '}' }}'`
{{ '}' }});
// Create item locally
const newItem = {{ '{' }}
title: 'Implement ElectricSQL',
completed: false,
userId: currentUserId
{{ '}' }};
await db.items.create(newItem);
// ✅ Available IMMEDIATELY locally, offline if needed
Local data model
ElectricSQL syncs data to a local database SQLite (browser/mobile) or OPFS (Origin Private File System) for web applications. This local database is a partial and opportunistic replica of the server: it contains only the data "subscribed" by the client. Thanks to selective sync (filtering with WHERE clause), you sync only relevant records—a personal todo list for 1000 users won't download all 1000 lists, just the current user's list.
// Sync only my blog posts
const {{ '{' }} posts {{ '}' }} = await db.syncTable('posts', {{ '{' }}
where: `authorId = '${{ '{' }}userId{{ '}' }}'`
{{ '}' }});
// Live local query
const allPosts$ = posts.live();
allPosts$.subscribe(list => {{ '{' }}
console.log('Posts updated:', list); // Re-render UI
{{ '}' }});
// Write offline
posts.create({{ '{' }}
title: 'ElectricSQL Rocks',
content: 'Lorem ipsum...',
authorId: userId
{{ '}' }});
// ✅ Applied INSTANTLY to local database
// ⏳ Syncs in background when online
Automatic conflict resolution
One of the thorniest problems in local-first is what happens when two devices modify the same record offline. ElectricSQL tackles this with CRDTs (Conflict-free Replicated Data Types) and causal ordering. Every operation receives a unique logical timestamp, and the server tracks causality between operations. If two clients modify the same field, ElectricSQL applies a resolution strategy (Last-Write-Wins by default, customizable) and all clients converge to the same final state with no data loss.
// Client A (offline): modify item
const item = await db.items.findUnique(itemId);
item.title = 'Task A - Offline now'; // Client A modifies
item.status = 'in_progress';
await db.items.update({{ '{' }}
where: {{ '{' }} id: itemId {{ '}' }},
data: item
{{ '}' }});
// Client B (online): modify SAME item
// (simultaneous scenario)
await fetch('/api/items/' + itemId, {{ '{' }}
method: 'PATCH',
body: JSON.stringify({{ '{' }}
title: 'Task A - Online only', // CONFLICT!
status: 'completed'
{{ '}' }})
{{ '}' }});
// Result: ElectricSQL CONVERGES automatically
// Strategy: Last-Write-Wins + field merge
// title: 'Task A - Online only' (client B wrote later)
// status: 'completed' (all agree)
// ✅ Zero data loss, zero visible conflicts
Application integration
ElectricSQL provides client libraries for TypeScript, React, React Native, and Svelte. Integration is declarative: use a hook (React) or store (Svelte) to subscribe to data changes, and the UI updates automatically. No fetch management, caching, invalidation—it's all baked-in.
import {{ '{' }} useElectric {{ '}' }} from 'electric-sql/react';
import {{ '{' }} useState {{ '}' }} from 'react';
export function TodoList() {{ '{' }}
const db = useElectric();
const {{ '{' }} items {{ '}' }} = db.useQuery(
db.items.findMany({{ '{' }} where: {{ '{' }} completed: false {{ '}' }} {{ '}' }})
);
const [title, setTitle] = useState('');
const handleAdd = async () => {{ '{' }}
await db.items.create({{ '{' }} title, completed: false {{ '}' }});
setTitle('');
// ✅ UI re-renders AUTOMATICALLY (live reactive query)
{{ '}' }};
return (
<div>
<input
value={{ '{' }}title{{ '}' }}
onChange={{ '{' }}(e) => setTitle(e.target.value){{ '}' }}
/>
<button onClick={{ '{' }}handleAdd{{ '}' }}>Add</button>
<ul>
{{ '{' }}items?.map(item => (
<li key={{ '{' }}item.id{{ '}' }}>{{ '{' }}item.title{{ '}' }}</li>
)){{ '}' }}
</ul>
</div>
);
{{ '}' }}
Performance and scalability
ElectricSQL is designed to scale to thousands of concurrent clients. Each client syncs only the data it needs (selective replication), dramatically reducing network bandwidth and local storage. The server uses native PostgreSQL (no special replica), so all standard SQL indexes, triggers, and procedures work. Local read performance (SQLite) is in the microsecond range—hundreds of times faster than a remote API call.
Comparison with other local-first solutions
Other platforms like Firestore, Realm, and CouchDB offer sync but have different tradeoffs. Firestore is cloud-only and non-standard SQL. Realm is a specialized mobile database, not web-first. CouchDB is a non-relational document store. ElectricSQL stands out by keeping PostgreSQL as source of truth, letting you use standard SQL queries and the entire PostgreSQL ecosystem (extensions, full-text search, JSON, geospatial, etc.). You're not locked into a proprietary vendor.
Ideal use cases
ElectricSQL shines in scenarios where:
✅ Offline-first apps — Notes, todos, calendars that work flawlessly without network.
✅ Low latency critical — Writes are instant in local database, no server roundtrip wait.
✅ Limited bandwidth — Mobile on 2G/3G or environments where bandwidth is expensive.
✅ Real-time collaboration — Multiple users modify same data, ElectricSQL handles conflicts.
✅ Hybrid online/offline apps — Switch between online and offline without code changes.
Implementation best practices
When using ElectricSQL, consider these principles:
1. Sync selectively — Don't subscribe to entire tables. Use WHERE to filter only relevant records.
2. Understand causality — ElectricSQL preserves causal ordering but not global ordering.
If A modifies X, then B modifies Y, locally you might see Y before X if network latency varies.
3. Handle sync errors — Network fails. Write UI that communicates when offline,
and auto-retry.
4. Use local transactions — ElectricSQL supports ACID transactions in local database.
Group related operations.
Limitations and considerations
ElectricSQL is not a universal solution. Known limitations:
❌ Requires relational schema — Doesn't work well with complex hierarchical data or nested documents.
❌ Opportunistic sync — If client stays offline for days, sync at reconnection might be slow.
❌ No remote join queries — You sync tables, not join results. Must join locally.
❌ Memory management — Large local database consumes memory. Consider pagination or lazy loading.
Setup and configuration
Getting started with ElectricSQL is straightforward. Install the npm client, configure the server (self-hosted or cloud), and define schema with standard SQL.
# Install TypeScript client
npm install electric-sql
# Configure server (Docker)
docker run -e DATABASE_URL=postgres://user:pass@postgres:5432/app \
-e ELECTRIC_ALLOW_DIRTY_DEFAULT=true \
electricsql/electric:latest
# In your TypeScript project
import {{ '{' }} ElectricDatabase {{ '}' }} from 'electric-sql';
const db = new ElectricDatabase('myapp.db');
await db.connect({{ '{' }}
host: 'localhost',
port: 5133,
database: 'myapp'
{{ '}' }});
// ✅ Ready to sync
Conclusion
ElectricSQL represents a mature and pragmatic approach to local-first: it keeps PostgreSQL as foundation (you can use the entire SQL ecosystem), syncs in real-time, resolves conflicts automatically, and lets clients operate offline without compromises in user experience. It's not a universal solution (relational schema, opportunistic sync), but for applications that fit its use cases (offline-first, low latency, limited bandwidth, collaboration), it's incredibly effective. If you're building a modern app where offline experience is critical and data consistency is non-negotiable, ElectricSQL deserves to be in your toolkit.
🎯 Key takeaways
- ElectricSQL syncs PostgreSQL with local client in real-time
- Complete offline capability + auto-sync on connection restore
- CRDTs and causal consistency for zero-touch conflict resolution
- Selective sync: download only relevant data
- Simple integration with React, TypeScript, React Native
- Ideal for offline-first, low latency, real-time collaboration
Frequently asked questions
Does ElectricSQL require a dedicated PostgreSQL database?
Yes, ElectricSQL requires a PostgreSQL server. It's not a new database, but a sync layer built on PostgreSQL as source of truth. You can use self-hosted PostgreSQL or cloud (Render, Railway, Supabase).
What happens if two clients modify the same record offline?
ElectricSQL uses CRDTs and causal ordering to converge automatically. It applies a resolution strategy (Last-Write-Wins by default), ensuring all clients see the same final result without visible conflicts.
Does ElectricSQL work with SQLite in the browser?
Yes, ElectricSQL uses SQLite for local database in browser (via OPFS or emscripten). It offers ACID transactions locally and read performance in the microsecond range.
Can I use ElectricSQL with React Native?
Yes, ElectricSQL supports React Native. Local database uses native SQLite (via react-native-sqlite), and integration is with standard React hooks.
How much data can ElectricSQL sync?
ElectricSQL uses selective sync, so you sync only relevant data for the client. An app with 1000 users won't download the entire database, just the current user's records. Local storage is limited by SQLite/browser (typically GB).

Commenti
Caricamento commenti...
Sign in to comment