@livestore/react package provides first-class React bindings for LiveStore. It exposes a Suspense-based store hook, reactive query hooks, and a registry for managing multiple store instances.
Features
- Fine-grained reactivity using LiveStore’s signals-based system
- Synchronous, instant query results — no
useEffectorisLoadingchecks - Suspense-based loading with
ErrorBoundarysupport - Multiple isolated store instances via
StoreRegistry - Transactional state transitions via
batchUpdates - Works with Expo / React Native via
@livestore/adapter-expo
Installation
Setup
Configure the store
Create a store configuration file and export a custom hook wrapping
useStore(). The hook suspends while the store is loading, so components that call it must be inside a <Suspense> boundary.store.ts
Set up the registry
Create a
StoreRegistry and provide it via <StoreRegistryProvider>. Wrap with <Suspense> and <ErrorBoundary> to handle loading and error states.App.tsx
Querying data
Usestore.useQuery() to subscribe to reactive queries. The component re-renders automatically when the query result changes.
CompletedTodos.tsx
store.useQuery() accepts any Queryable: a QueryBuilder, LiveQueryDef, SignalDef, or LiveQuery instance.
Client documents
Usestore.useClientDocument() for client-only state that is not synced. It works like React.useState but is backed by a State.SQLite.clientDocument() table.
TodoItem.tsx
store.useClientDocument() returns a [row, setRow, id, query$] tuple. The id argument is optional when the table has a default id.
Sync status
Usestore.useSyncStatus() to subscribe to real-time sync connection status.
SyncIndicator.tsx
Multiple stores
Use thestoreOptions() helper to define reusable, type-safe store configurations, then create multiple instances with different storeId values.
issue.store.ts
IssueView.tsx
Preloading stores
When you know a store will be needed soon, preload it to warm up the cache before the user navigates.PreloadedIssue.tsx
Logging
Customize the logger and log level for debugging:store-with-logging.ts
LogLevel.None to disable logging entirely.
Framework compatibility
Vite
Vite
LiveStore works with Vite out of the box.
TanStack Start
TanStack Start
Place
<StoreRegistryProvider> in the component prop of createRootRoute, not inside shellComponent. The shellComponent can re-render on navigation, which would remount LiveStore and show the loading screen on every page transition.app/routes/__root.tsx
Expo / React Native
Expo / React Native
LiveStore supports Expo and React Native via
@livestore/adapter-expo. See the platform adapters page for setup details.Next.js
Next.js
Next.js is not yet supported out of the box due to its SSR constraints.
API reference
storeOptions(options)
Helper for defining reusable store configurations with full type inference. Returns options that can be passed to useStore() or storeRegistry.preload().
| Option | Type | Description |
|---|---|---|
storeId | string | Unique identifier for this store instance |
schema | Schema | The LiveStore schema |
adapter | Adapter | The platform adapter |
unusedCacheTime | number | Time in ms to keep unused stores in cache (default: 60_000 in browser, Infinity otherwise) |
batchUpdates | function | Function for batching React updates (recommended) |
boot | function | Called when the store is loaded |
onBootStatus | function | Callback for boot status updates |
context | object | User-defined context for dependency injection |
syncPayload | object | Payload sent to sync backend (e.g., auth tokens) |
logger | Logger | Custom logger implementation |
logLevel | LogLevel | Minimum log level |
disableDevtools | boolean | 'auto' | Whether to disable devtools (default: 'auto') |
useStore(options)
Returns a store instance augmented with React hooks (store.useQuery(), store.useClientDocument(), store.useSyncStatus()).
- Suspends until the store is loaded.
- Throws if loading fails (caught by
ErrorBoundary). - Stores are cached by
storeIdin theStoreRegistry. Multiple calls with the samestoreIdreturn the same instance. - Store options are applied only on first load; changing options on a cached store has no effect.
store.commit(...events)
Commits one or more events to the store.
store.useQuery(queryable)
Subscribes to a reactive query. Re-renders the component when the result changes. Accepts any Queryable: QueryBuilder, LiveQueryDef, SignalDef, or LiveQuery instance.
store.useClientDocument(table, id?, options?)
React.useState-like hook for State.SQLite.clientDocument() tables. Returns a [row, setRow, id, query$] tuple.
store.useSyncStatus()
Subscribes to sync status changes. Re-renders when the status changes.
new StoreRegistry(config?)
Creates a registry that manages store loading, caching, and disposal.
| Config option | Description |
|---|---|
defaultOptions.batchUpdates | Default batching function for all stores |
defaultOptions.unusedCacheTime | Default cache retention time |
defaultOptions.disableDevtools | Whether to disable devtools by default |
runtime | Effect runtime for registry operations |
<StoreRegistryProvider>
Context provider that makes a StoreRegistry available to descendant components via useStoreRegistry().
| Prop | Description |
|---|---|
storeRegistry | The registry instance |
useStoreRegistry(override?)
Returns the StoreRegistry from the nearest <StoreRegistryProvider>, or the override if provided.
storeRegistry.preload(options)
Loads a store without suspending to warm up the cache. Returns a Promise that resolves when loading completes.