Architecture overview
LiveStore has a pluggable architecture. Three extension points let you adapt it to any platform, framework, or sync backend:Platform adapters
Integrate LiveStore with web browsers, Expo (React Native), Node.js, and other runtime environments.
Framework integrations
Use LiveStore from React, Vue, Solid, Svelte, or any framework with the core store API.
Sync providers
Choose or build a sync backend — Cloudflare, ElectricSQL, S2, or a custom implementation.
Adapter (platform adapter)
An adapter knows how to instantiate a client session for a given runtime environment. It abstracts over the differences between platforms — threading models, storage APIs, worker APIs — so the rest of LiveStore can stay platform-agnostic. Available adapters:@livestore/adapter-web— browser environments (uses a Web Worker for the leader thread)@livestore/adapter-expo— Expo / React Native (iOS, Android)@livestore/adapter-node— Node.js (server, CLI, agent workloads)
Client and client session
Client
A client is a logical grouping of one or more client sessions. It is identified by aclientId — a randomly generated 6-character nanoid. Sessions within the same client share local persisted data.
Client session
A client session is a single running instance within a client. It is identified by asessionId. In a web browser, a sessionId can persist across tab reloads, and multiple tabs of the same app each have their own session within the same client.
Each client session contains:
- A store — the main API surface for your application
- A reactivity graph — the subscription system that drives UI updates
LiveStore does not have built-in concepts of “users” or “devices”. User identity must be modeled within your application domain through events and application logic. The
clientId identifies a client instance, not a user — multiple clients can represent the same user on different browsers or devices.Store
The store is the main entry point for using LiveStore in your application. It exposes thecommit method for writing events, useQuery for reactive reads, and access to sync status and devtools.
To create a store you provide a schema and a platform adapter:
useStore from @livestore/react).
A store is identified by a storeId, which is also used when syncing events between clients — clients with the same storeId share the same eventlog.
Store API
Full reference for
createStore options and the store instance APIReact integration
How to set up and access the store in React components
Events and eventlog
Events
Events are immutable facts that describe something that happened in your application — for example,TodoCreated or TodoCompleted. They are defined with a name and an Effect Schema for their payload.
There are two kinds of events:
| Kind | Description |
|---|---|
Events.synced | Persisted to the eventlog and synced to other clients via the sync backend |
Events.clientOnly | Applied locally in memory only — never persisted or synced |
clientOnly events for transient UI state (selected item, open panels, filter values) that doesn’t need to survive a page refresh or sync to other users.
Eventlog
The eventlog is the ordered, append-only log of all committed synced events. It is the canonical source of truth in LiveStore. All persisted state is a projection of this log. The eventlog gives you:- Persistence — events survive page refreshes and app restarts
- Sync — events replay identically on every client
- History — full audit trail of every change (enables time-travel debugging)
- Flexibility — change your queries without migrations by rematerializing
Events deep dive
Full reference for event definitions, the eventlog, naming conventions, and versioning
Schema
A schema is the complete definition of your app’s data model. It is the single configuration object you pass tomakeSchema and then to the store.
A schema has three parts:
Event definitions
The set of events that can happen in your app (
Events.synced and Events.clientOnly).schema.ts
Schema and events
Complete reference for defining events, tables, and the makeSchema API
State and materializers
How materializers work, SQLite column types, and state derivation
Reactivity system
LiveStore’s reactivity system keeps your UI in sync with the local SQLite database automatically. There are three primitives:queryDb
Creates a reactive SQL query. When any row that the query reads changes, subscribers are notified and UI components re-render.
signal
A signal is a reactive primitive holding a single value. It is useful for UI state that doesn’t live in SQLite (for example, the currently selected item ID).
computed
A computed value derives a reactive value from one or more other reactive sources (queries, signals, or other computed values).
Reactivity system deep dive
Full reference for queryDb, signals, computed, and the subscription model
Sync provider
A sync provider is a package that pairs a sync backend with a sync client. The sync backend is a central server that stores events and enforces a global total order. The sync client runs inside LiveStore and communicates with the backend. LiveStore ships with several sync providers:Cloudflare
Serverless sync via Cloudflare Workers and Durable Objects (
@livestore/sync-cf)ElectricSQL
Sync powered by ElectricSQL’s Postgres-based sync engine
S2
High-throughput sync via S2 streaming infrastructure
Custom
Build your own sync backend by implementing the LiveStore sync protocol
Framework integration
A framework integration is a package that provides reactive hooks and context providers for a specific UI framework. The integration manages store lifecycle (creation, cleanup) and bridges the LiveStore reactivity system to the framework’s rendering model. Available integrations: React (@livestore/react), Vue, Solid, and Svelte.
React integration
useStore, useQuery, and useSyncStatus for React applications