NOTE: LiveStore is still in beta and releases can include breaking changes.
See
state of the project
for more info. LiveStore is following a semver-like release strategy where
breaking changes are released in minor versions before the 1.0 release.
0.4.0 (Unreleased)
For v0.4.0 features, see the development documentation at dev.docs.livestore.dev which includes the latest documentation.
Installing v0.4.0 dev release: Use the dev tag to install the latest development version. Make sure all LiveStore packages use the same version:
New Cloudflare adapter: Added the Workers/Durable Object adapter and rewrote the sync provider so LiveStore ships WebSocket, HTTP, and Durable Object RPC transports as first-party Cloudflare options (#528, #591).
S2 sync backend: Map LiveStore’s event log onto S2’s durable stream store via @livestore/sync-s2, unlocking scalable basins/streams, SSE live tails, and transport-safe batching for large sync workloads (#292, #709).
Schema-first tables: LiveStore now accepts Effect schema definitions as SQLite table definitions, removing duplicate column configuration in applications (#544).
Cloudflare sync provider storage: Default storage is now Durable Object (DO) SQLite, with an explicit option to use D1 via a named binding. Examples and docs updated to the DO‑by‑default posture (see issue #266, #693).
MCP support: LiveStore now ships a CLI with a first-class MCP server so automation flows can connect to instances, query data, and commit events using the bundled tools (#705).
React multi-store API: The multi-store API is now the primary React integration, replacing <LiveStoreProvider> with <StoreRegistryProvider> and useStore() with store options. The new API supports multiple stores, preloading, and caching out of the box. See the React integration docs (#841).
Breaking Changes
store.shutdown API: The shutdown method now returns an Effect instead of a Promise. Use yield* store.shutdown() inside Effects or await store.shutdownPromise() when a Promise is needed.
// Before
await store.shutdown()
// After (Effect API)
yield* store.shutdown()
// Or use the Promise helper
await store.shutdownPromise()
store.subscribe callback signature: The subscription callback is now passed as the second argument, with subscription options moved to an optional third argument object. Replace usages like store.subscribe(query$, { onUpdate }) with store.subscribe(query$, onUpdate, options).
QueryBuilder.first() behaviour:table.query.first() now returns undefined when no rows match. To keep the old behaviour, pass { behaviour: "error" }, or supply a fallback.
wa-sqlite version alignment:@livestore/wa-sqlite now follows LiveStore’s versioning scheme to keep adapters on matching releases.
Terminal window
# Before: wa-sqlite had independent versioning
pnpmaddwa-sqlite@1.0.5
# After: wa-sqlite follows LiveStore versioning
pnpmadd@livestore/wa-sqlite@dev
This change affects projects that directly depend on wa-sqlite. Most users rely on it indirectly through LiveStore adapters and don’t need to change anything.
Cloudflare sync provider storage selection: Removed implicit D1 auto‑selection and env‑based fallbacks. D1 must be selected explicitly via storage: { _tag: 'd1', binding: 'DB' } on the sync Durable Object. The default is DO SQLite (see issue #266, #693).
To use the default DO SQLite, omit the storage option or pass { _tag: 'do-sqlite' }.
Restructured LiveStoreEvent and EventSequenceNumber APIs: Types are now organized into symmetric Global, Client, and Input namespaces that clarify the distinction between sync backend format, client format, and events without sequence numbers (#855):
Error class rename:UnexpectedError has been renamed to UnknownError for better semantic clarity and consistency with Effect ecosystem naming conventions. The previous name conflicted with Effect’s terminology where “unexpected errors” refer to defects, while this error type represents errors of unknown type from external libraries or infrastructure failures (See PR #823).
React integration API: The multi-store API is now the primary React integration, replacing <LiveStoreProvider> and the old useStore(). The new API uses StoreRegistry, <StoreRegistryProvider>, and useStore() with store options. See the React integration docs for full details (#841).
Removed top-level React hook exports:useQuery, useQueryRef, and useClientDocument are no longer exported at the top level from @livestore/react. Use the store methods instead (#946):
Type exports (UseClientDocumentResult, Dispatch, SetStateAction, etc.) remain available.
S2 proxy helper signature changes: The getSSEHeaders and getPushHeaders functions in @livestore/sync-s2/s2-proxy-helpers now accept an S2Config object instead of a token string. This enables s2-lite support via the new lite flag which adds the S2-Basin header for self-hosted S2 deployments (#978).
lite: true, // Adds S2-Basin header for s2-lite routing
}
Changes
Platform adapters
LiveStore now runs natively on Cloudflare Workers through the @livestore/adapter-cloudflare package. Durable Objects handle coordination and D1 provides persistence, enabling globally distributed applications with local-first behaviour. The adapter provides:
Stateful Durable Objects for LiveStore instances
D1 database integration for event persistence
Hibernate-friendly architecture to minimise compute costs
Direct integration with the Cloudflare sync provider
Cloudflare adapter: Added a development-only reset persistence option to clear Durable Object state (#664).
Node and Expo adapters: Added development-only reset persistence options to clear local state (#654).
Web adapter: Archive development state databases with bounded retention to avoid OPFS exhaustion (#649, thanks @IGassmann).
Sync provider
The @livestore/sync-cf package has been rewritten to offer three first-party transports—WebSocket, HTTP, and Durable Object RPC—so Cloudflare deployments can choose the right balance of latency and infrastructure support:
WebSocket transport: Bidirectional real-time communication with automatic reconnection
HTTP transport: Request/response sync with polling for environments that can’t keep WebSocket connections open
Durable Object RPC: Direct Durable Object calls that avoid network overhead entirely
Key improvements include streaming pull operations (faster initial sync), a two-phase sync (bulk transfer followed by real-time updates), improved error recovery, and comprehensive test coverage.
Storage engine configuration: Default storage is DO SQLite; configure D1 explicitly with storage: { _tag: 'd1', binding: '<binding>' }. Removed env‑based fallback detection. Docs include a “Storage engines” section, and examples default to DO storage (see issue #266).
DO SQLite insert batching: Adjusted insert chunk size to stay under parameter limits for large batches.
WebSocket transport: Introduced message chunking limits to stay under platform constraints (#687).
Reliability: Retry and backoff on push errors, restart push on advance, and add regression tests (#639).
Resilience: Improve sync provider robustness and align test helpers for CI and local development (#682, #646).
Header forwarding: Added forwardHeaders option to makeDurableObject() for cookie-based authentication. Headers are stored in WebSocket attachments to survive hibernation and accessible via context.headers in onPush/onPull callbacks (#929).
Backend reset detection: LiveStore now detects when a sync backend has been reset and handles it based on the onBackendIdMismatch option in SyncOptions. Default behaviour ('reset') clears local storage and shuts down so the app can restart with fresh data. Alternative modes include 'shutdown' (shut down without clearing) and 'ignore' (continue with stale data). See Backend Reset Detection docs (#980).
S2 sync backend
LiveStore now ships @livestore/sync-s2, a first-party integration with S2—the stream store that exposes basins and append-only streams over HTTP and SSE. LiveStore maps each storeId onto its own S2 stream while keeping LiveStore’s logical sequencing inside the payload, so teams gain provider-managed durability, retention policies, and elastic fan-out without retooling their event model (#292). The provider still expects an authenticated proxy that provisions basins/streams, forwards LiveStore pushes and pulls, and translates S2 cursors back into LiveStore metadata.
Stream primitives: Helper utilities (ensureBasin(), ensureStream(), makeS2StreamName()) manage S2 provisioning and naming so apps can wire up a single /api/s2 entry point without manual HTTP plumbing (#292).
Live pull over SSE: The client understands S2’s batch, ping, and error SSE events, keeping live cursors in sync while avoiding dropped connections and manual tail loops (#292).
Transport-safe batching: Append helpers respect S2’s 1 MiB / 1000-record limits, preventing 413 responses while you stream large batches into managed storage (#709).
s2-lite support: Added support for s2-lite, the open-source self-hosted S2. Set lite: true in S2Config to enable header-based basin routing. CI tests now run against s2-lite, removing the dependency on hosted S2 credentials (#978).
Event log lookup optimization: Improved event log lookup performance for large unsynced logs, speeding startup time (#1012).
Unknown event handling: Schemas now ship an unknownEventHandling configuration so older clients can warn, ignore, fail, or forward telemetry when they see future events while keeping the eventlog intact (#353).
Schema-first tables: LiveStore now accepts Effect schema definitions as SQLite table inputs, keeping type information and stored schema in the same place. For example:
This keeps the schema as a single source of truth, enforces types at compile time, and removes duplicate column definitions.
Materializer hash checks: Development builds compute hashes for materializer output and raise MaterializerHashMismatchError when handlers diverge, catching non-pure implementations before they reach production.
Pure materializers ensure deterministic replay during sync, improve test reliability, and make debugging predictable.
Event deprecation support: Mark entire events or individual fields as deprecated to guide schema evolution. When deprecated events are committed or deprecated fields have values, a warning is logged via Effect’s logging system to help teams migrate away from legacy patterns (#956).
Store:store.networkStatus now surfaces sync backend connectivity so apps can read the latest status or subscribe directly; the signal is no longer re-exposed on client sessions (livestorejs/livestore#394).
LiveStoreSchema.Any type alias simplifies schema composition across adapters.
Query builder const assertions improve type inference, and store.subscribe() now accepts query builders (#371, thanks @rgbkrk).
Store.subscribe async iteration: The async iterator overload now exposes a first-class AsyncIterable so for await loops work without manual casts, and the new exported Queryable type documents the accepted inputs (livestorejs/livestore#736).
Queryable type export:packages/@livestore/livestore now re-exports Queryable<TResult> so shared utilities and framework adapters can describe the exact shapes accepted by store.subscribe and subscribeStream (livestorejs/livestore#736).
Store operations after shutdown are rejected with a descriptive UnknownError. Shutdown now returns an Effect (see breaking changes).
Exact optional property types are enabled, surfacing missing optional handling at compile time (#600).
Effect Equal and Hash implementations for LiveQueryDef and SignalDef improve comparisons.
Sync payload and store ID are exposed to onPull/onPush handlers (#451).
Materializers receive each event’s clientId, simplifying multi-client workflows (#574).
React peer dependency relaxed from exact to caret range for smoother upgrades (#621).
Effect integration: Added Store.Tag(schema, storeId) API for idiomatic Effect usage. Returns a yieldable Context.Tag with static accessors (query, commit, use) and a layer() factory method. The previous makeStoreContext() and LiveStoreContextLayer() APIs are now deprecated:
import { Store } from'@livestore/livestore/effect'
WAL snapshot guard:@livestore/sqlite-wasm now aborts WAL-mode snapshot imports with an explicit SqliteError, preventing silent corruption when loading backups (#694).
New example: CF Chat: A Cloudflare Durable Objects chat example demonstrates WebSocket sync, reactive message handling, and bot integrations across client React components and Durable Object services.
Cloudflare examples now default to DO SQLite storage. D1 usage is documented via an explicit binding and a one‑line storage option in code.
Cloudflare Workers deployments:mono examples deploy now provisions Worker targets so DO-backed demos stay current across prod and dev environments (#690, #735).
Add Netlify dev deployments for examples to simplify testing (#684).
Svelte integration docs: Added the Svelte framework guide plus the Svelte TodoMVC example so @livestore/svelte is documented alongside React and Solid.
Use Twoslash for select getting started snippets in docs (#658).
TanStack Start examples:web-linearlite, web-todomvc-sync-electric, and web-todomvc-sync-s2 now run on TanStack Start with Vite 7 compatibility fixes and Cloudflare runtime flags (#747).
Docs for coding agents: Documentation now serves agent-optimised Markdown so automations get concise answers without burning unnecessary tokens (#715).
TypeScript-validated snippets: Most examples are now type checked through the Twoslash pipeline enabling in-docs intellisense (#715).
Experimental features
LiveStore CLI for project scaffolding (experimental preview, not production-ready)
Updated (peer) dependencies
Effect updated to 3.17.14
React updated to 19.1.1
Vite updated to 7.1.7
TypeScript 5.9.2 compatibility
Internal Changes
Updates in this section are primarily relevant to maintainers and contributors. They cover infrastructure, tooling, and other non-user-facing work that supports the release.
Core Runtime
Encapsulated Store internals behind StoreInternalsSymbol (moved boot, syncProcessor, effectContext, tableRefs, otel, sqliteDbWrapper, clientSession, activeQueries, reactivityGraph, isShutdown), reducing public surface and clarifying API boundaries (#814).
Testing Infrastructure
Comprehensive sync provider test suite with property-based testing (#386)
Node.js sync test infrastructure with Wrangler dev server integration (#594)
Parallel CI test execution reducing test time significantly (#523)
Cloudflare sync provider tests run against both storage engines (D1 and DO SQLite) using separate wrangler configs.
Development Tooling
Strict peer dep composition: Added @effect/vitest to utilsEffectPeerDeps and @livestore/peer-deps, and deduplicated the peer-deps package to derive its dependency list from the canonical utilsEffectPeerDeps source (#1107).
Migration from ESLint to Biome for improved performance (#447)
Add GitHub issue templates to improve issue quality (#602)
Reworked the documentation tooling so maintainers continuously publish token-efficient, TypeScript-backed snippets that stay reliable for coding agents (#715)
Snapshot release confirmation prompt: The mono release snapshot command now prompts for confirmation before publishing. Pass --yes to skip the prompt in scripts and CI. The prompt is also auto-skipped when CI is set (#1049).
wa-sqlite Integration
The wa-sqlite WebAssembly SQLite implementation has been integrated directly into the LiveStore monorepo as a git subtree under packages/@livestore/wa-sqlite. This change provides several benefits:
Direct control over SQLite builds and customizations for LiveStore’s needs
Simplified dependency management and version alignment
Ability to apply LiveStore-specific patches and optimizations
Reduced external dependency risks and improved build reproducibility
Key changes:
Integrated wa-sqlite as git subtree, replacing external npm dependency (#582)
Ported build scripts and test infrastructure to LiveStore monorepo (#572)
Updated to SQLite 3.50.4 with LiveStore-optimized configuration (#581)
Fixed test setup issues and improved reliability (#583)
This integration lays the foundation for future SQLite optimizations specific to LiveStore’s event-sourcing and sync requirements.
Note: Currently uses the @livestore/sqlite-wasm build but the plan is to
move to a native SQLite build in the future to improve performance and
reduce bundle size.
Still lacks a few devtools-related flows (e.g. graceful import/reset)
Breaking @livestore/adapter-web: Renamed makeAdapter to
makePersistedAdapter
Breaking @livestore/adapter-expo: Renamed makeAdapter to
makePersistedAdapter
Breaking: Renamed localOnly to clientOnly in table/mutation definitions.
Breaking: Renamed makeBackend to backend in sync options.
Breaking @livestore/react: useClientDocument now only works with for
tables with client-only derived mutations.
Breaking: Instead of calling query$.run() / query$.runAndDestroy(), please
use store.query(query$) instead.
Breaking: Removed store.__execute from Store.
Breaking: Removed globalReactivityGraph and explicit passing of
reactivityGraph to queries.
Breaking: Removed persisted option from store.commit. This will be
superceded by
eventlog compaction in
the future.
Breaking: The new syncing implementation required some changes to the storage
format. The liveStoreStorageFormatVersion has been bumped to 3 which will
create new database files.
Breaking: Moved queryGraphQL to @livestore/graphql and thus removing
graphql from peer dependencies of @livestore/livestore.
Moved dev helper methods from e.g. store.__devDownloadDb() to
store._dev.downloadDb()
Breaking @livestore/sync-cf: Renamed makeWsSync to makeWsSync
Notable improvements & fixes
Added support for write queries in the query builder
Introduced @livestore/peer-deps package to simplify dependency management
for Livestore packages if you don’t want to manually install all the peer
dependencies yourself.
Improved documentation (still a lot of work to do
here)
Shows a browser dialog when trying to close a tab/window with unsaved changes
The SQLite leader database now uses the WAL mode to improve performance and
reliability. (Thanks @IGassmann for the
contribution #259.)
Improve Otel tracing integration
Fix: The query builder now correctly handles IN and NOT IN where
operations
Fix: LiveStore crashes when using reserved keywords as a column name (from)
#245
Devtools
Changed devtools path from /_devtools.html to /_livestore
General connection stability improvements
Improved sync view:
See sync heads in real-time
Connect/disconnect button
Improved eventlog view:
Client-only mutations are now highlighted
Added clientId / sessionId columns
Grouped slow queries and live queries under new queries tab
Added SQLite query playground
Fix: Data browser now more clearly highlights selected table #239
Examples
Reworked the Linearlite React example. (Thanks
@lukaswiesehan for the contribution #248.)
Adjusted mutation names to use past-tense
Added Otel to todomvc and todomvc-sync-cf example
Internal changes
Embraced git-style push/pull semantics to sync mutations across the system
Added node syncing integration tests
Got rid of the coordinator abstraction in favour of a clear separation between
leader and client sessions
Renamed from EventId.local to EventSequenceNumber.client
Added @livestore/sqlite-wasm package which wraps @livestore/wa-sqlite and
exposes web and Node.js compatible VFS implementations
New devtools protocol via webmesh
Should improve reliability of devtools connection (particularly during app
reloads)
Large refactoring to share more code between adapters
Renamed SynchronousDatabase to SqliteDb
Upgrade to TypeScript 5.8
Upgraded dependencies
Now supports React 19
effect (needs to be 3.15.2 or higher)
@livestore/wa-sqlite (needs to be 1.0.5)
Still todo:
Release
Write blog post
Prepare X/Bluesky thread
After release:
Refactor: Get rid of sql-queries module
API improvement: Get rid of queryDb by exposing live queries directly on the
query builder / state primitives
Optimization: Bring back rehydrating via in-memory database (requires both app
and mutation db to be in-memory)
Web adapter:
Bug:
NotReadableError: The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.
Refactor shared-worker
Make it optional (for Android support)
Make it store-agnostic (so it’s reused across store instances)
Remove extra broadcast channel for session info in @livestore/adapter-web
Bug fix + testing: SQLite rollback error:
RuntimeError: null function or function signature mismatch, "note": "Failed calling makeChangeset.apply
(needs repro info, probably requires property testing)
Syncing:
introduce a way to know when an event is confirmed by the sync backend
when no sync backend is configured, the leader sync state should not keep
pending events in memory
Testing: Improve sync testing (prop testing): introduce arbitrary latency
for any kind of async step (~ chaos testing)
More graceful handling when receiving a event that doesn’t exist in the
local schema
This can happen if a new app version with a new schema and an old client
with the old schema tries to sync
2 solution paths:
Render “upgrade app” screen
Go offline until user upgrades the app
Clients should detect and gracefully handle when a sync backend resets its
eventlog (e.g. during debugging)
possibly introduce a eventlog id in the global sync metadata
@livestore/sync-cf:
Opening the snyc HTTP endpoint in the browser should provide a helpful
message
use http for initial pull while WS connection is established
Adjust networking protocol to embrace a “walk” flow similar to how
ElectricSQL’s protocol works. i.e. instead of doing 1 pull-req and getting
n pull-res back, we will adjust this to be 1:1 at the expense of slightly
higher round tripping overhead
We will “downgrade” the purpose of the remaining field to be only used
for UX purposes but not for correctness purposes. For correctness we
will only stop pull-walking when we get an empty array back.
Only use DO for write operations and pokes, use a separate way for
pull-reqs
Bring back “broadcast” pull res terminology
@livestore/sync-electric:
fix: connectivity state + offline handling
implement sync payload
Expo adapter: Fix memory leak in certain cases (needs repro info)
Refactor/improve event sequence number implementation
Current pain points/suboptimalities:
syncstate.ts: branching for global/client-only events
Get rid of leaderMergeCounterTable in favour of client-only merge
generation
Idea: Embed merge generation in the client-only event sequence number
Implementation detail: New event id strategy (uses a global event id integer
sequence number and each event also keeps a reference to its parent event
id)
React integration
Breaking: The React integration has been moved into a new separate package:
@livestore/react (before: @livestore/livestore/react)
Breaking: Renamed useTemporaryQuery to useScopedQuery
Web adapter
Devtools address is now automatically logged during development making
connecting easier.