Getting started with LiveStore + React
Prerequisites
Section titled “Prerequisites”- Recommended: Bun 1.2 or higher
- Node.js 23.0.0 or higher
Option A: Quick start
Section titled “Option A: Quick start”For a quick start, we recommend using our template app following the steps below.
For existing projects, see Existing project setup.
-
Set up project from template
Terminal window bunx tiged "github:livestorejs/livestore/examples/web-todomvc-sync-cf#dev" livestore-appTerminal window pnpm dlx tiged "github:livestorejs/livestore/examples/web-todomvc-sync-cf#dev" livestore-appTerminal window npx tiged "github:livestorejs/livestore/examples/web-todomvc-sync-cf#dev" livestore-appReplace
livestore-app
with your desired app name. -
Install dependencies
It’s strongly recommended to use
bun
orpnpm
for the simplest and most reliable dependency setup (see note on package management for more details).Terminal window bun installTerminal window pnpm installTerminal window npm installPro tip: You can use direnv to manage environment variables.
-
Run dev environment
Terminal window bun devTerminal window pnpm devTerminal window npm run dev -
Open browser
Open
http://localhost:60000
in your browser.You can also open the devtools by going to
http://localhost:60000/_livestore
.
Option B: Existing project setup
Section titled “Option B: Existing project setup”-
Install dependencies
Terminal window bun install @livestore/livestore@0.4.0-dev.7 @livestore/wa-sqlite@0.4.0-dev.7 @livestore/adapter-web@0.4.0-dev.7 @livestore/react@0.4.0-dev.7 @livestore/peer-deps@0.4.0-dev.7 @livestore/sync-cf/client@0.4.0-dev.7 @livestore/devtools-vite@0.4.0-dev.7Terminal window pnpm add @livestore/livestore@0.4.0-dev.7 @livestore/wa-sqlite@0.4.0-dev.7 @livestore/adapter-web@0.4.0-dev.7 @livestore/react@0.4.0-dev.7 @livestore/peer-deps@0.4.0-dev.7 @livestore/sync-cf/client@0.4.0-dev.7 @livestore/devtools-vite@0.4.0-dev.7Terminal window npm install @livestore/livestore@0.4.0-dev.7 @livestore/wa-sqlite@0.4.0-dev.7 @livestore/adapter-web@0.4.0-dev.7 @livestore/react@0.4.0-dev.7 @livestore/peer-deps@0.4.0-dev.7 @livestore/sync-cf/client@0.4.0-dev.7 @livestore/devtools-vite@0.4.0-dev.7 -
Update Vite config
Add the following code to your
vite.config.js
file:vite.config.js import { spawn } from 'node:child_process'import { livestoreDevtoolsPlugin } from '@livestore/devtools-vite'import react from '@vitejs/plugin-react'import { defineConfig } from 'vite'export default defineConfig({server: {port: process.env.PORT ? Number(process.env.PORT) : 60_001,fs: { strict: false },},worker: { format: 'es' },plugins: [react(),livestoreDevtoolsPlugin({ schemaPath: './src/livestore/schema.ts' }),// Running `wrangler dev` as part of `vite dev` needed for `@livestore/sync-cf`{name: 'wrangler-dev',configureServer: async (server) => {const wrangler = spawn('./node_modules/.bin/wrangler', ['dev', '--port', '8787'], {stdio: ['ignore', 'inherit', 'inherit'],})const shutdown = () => {if (wrangler.killed === false) {wrangler.kill()}process.exit(0)}server.httpServer?.on('close', shutdown)process.on('SIGTERM', shutdown)process.on('SIGINT', shutdown)wrangler.on('exit', (code) => console.error(`wrangler dev exited with code ${code}`))},},],})
Define Your Schema
Section titled “Define Your Schema”Create a file named schema.ts
inside the src/livestore
folder. This file defines your LiveStore schema consisting of your app’s event definitions (describing how data changes), derived state (i.e. SQLite tables), and materializers (how state is derived from events).
Here’s an example schema:
import { import Events
Events, const makeSchema: <TInputSchema extends InputSchema>(inputSchema: TInputSchema) => FromInputSchema.DeriveSchema<TInputSchema>
makeSchema, import Schema
Schema, type SessionIdSymbol = typeof SessionIdSymbolconst SessionIdSymbol: typeof SessionIdSymbol
Can be used in queries to refer to the current session id.
Will be replaced with the actual session id at runtime
In client document table:
const uiState = State.SQLite.clientDocument({ name: 'ui_state', schema: Schema.Struct({ theme: Schema.Literal('dark', 'light', 'system'), user: Schema.String, showToolbar: Schema.Boolean, }), default: { value: defaultFrontendState, id: SessionIdSymbol },})
Or in a client document query:
const query$ = queryDb(tables.uiState.get(SessionIdSymbol))
SessionIdSymbol, import State
State } from '@livestore/livestore'
// You can model your state as SQLite tables (https://docs.livestore.dev/reference/state/sqlite-schema)export const const tables: { todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; }; }>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>; uiState: State.SQLite.ClientDocumentTableDef<...>;}
tables = { todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; };}>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>
todos: import State
State.import SQLite
SQLite.function table<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { columnType: "integer"; ... 4 more ...; autoIncrement: false; };}, Partial<...>>(args: { ...;} & Partial<...>): State.SQLite.TableDef<...> (+2 overloads)
Creates a SQLite table definition from columns or an Effect Schema.
This function supports two main ways to define a table:
- Using explicit column definitions
- Using an Effect Schema (either the
name
property needs to be provided or the schema needs to have a title/identifier)
// Using explicit columnsconst usersTable = State.SQLite.table({ name: 'users', columns: { id: State.SQLite.text({ primaryKey: true }), name: State.SQLite.text({ nullable: false }), email: State.SQLite.text({ nullable: false }), age: State.SQLite.integer({ nullable: true }), },})
// Using Effect Schema with annotationsimport { Schema } from '@livestore/utils/effect'
const UserSchema = Schema.Struct({ id: Schema.Int.pipe(State.SQLite.withPrimaryKey).pipe(State.SQLite.withAutoIncrement), email: Schema.String.pipe(State.SQLite.withUnique), name: Schema.String, active: Schema.Boolean.pipe(State.SQLite.withDefault(true)), createdAt: Schema.optional(Schema.Date),})
// Option 1: With explicit nameconst usersTable = State.SQLite.table({ name: 'users', schema: UserSchema,})
// Option 2: With name from schema annotation (title or identifier)const AnnotatedUserSchema = UserSchema.annotations({ title: 'users' })const usersTable2 = State.SQLite.table({ schema: AnnotatedUserSchema,})
// Adding indexesconst PostSchema = Schema.Struct({ id: Schema.String.pipe(State.SQLite.withPrimaryKey), title: Schema.String, authorId: Schema.String, createdAt: Schema.Date,}).annotations({ identifier: 'posts' })
const postsTable = State.SQLite.table({ schema: PostSchema, indexes: [ { name: 'idx_posts_author', columns: ['authorId'] }, { name: 'idx_posts_created', columns: ['createdAt'], isUnique: false }, ],})
table({ name: "todos"
name: 'todos', columns: { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; };}
columns: { id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false;}
id: import State
State.import SQLite
SQLite.const text: <string, string, false, typeof NoDefault, true, false>(args: { schema?: Schema.Schema<string, string, never>; default?: typeof NoDefault; nullable?: false; primaryKey?: true; autoIncrement?: false;}) => { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false;} (+1 overload)
text({ primaryKey?: true
primaryKey: true }), text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false;}
text: import State
State.import SQLite
SQLite.const text: <string, string, false, "", false, false>(args: { schema?: Schema.Schema<string, string, never>; default?: ""; nullable?: false; primaryKey?: false; autoIncrement?: false;}) => { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false;} (+1 overload)
text({ default?: ""
default: '' }), completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false;}
completed: import State
State.import SQLite
SQLite.const boolean: <boolean, false, false, false, false>(args: { default?: false; nullable?: false; primaryKey?: false; autoIncrement?: false;}) => { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false;} (+1 overload)
boolean({ default?: false
default: false }), deletedAt: { columnType: "integer"; schema: Schema.Schema<Date | null, number | null, never>; default: None<never>; nullable: true; primaryKey: false; autoIncrement: false;}
deletedAt: import State
State.import SQLite
SQLite.const integer: <number, Date, true, typeof NoDefault, false, false>(args: { schema?: Schema.Schema<Date, number, never>; default?: typeof NoDefault; nullable?: true; primaryKey?: false; autoIncrement?: false;}) => { columnType: "integer"; schema: Schema.Schema<Date | null, number | null, never>; default: None<never>; nullable: true; primaryKey: false; autoIncrement: false;} (+1 overload)
integer({ nullable?: true
nullable: true, schema?: Schema.Schema<Date, number, never>
schema: import Schema
Schema.class DateFromNumber
Defines a schema that converts a number
into a Date
object using the new Date
constructor. This schema does not validate the numerical input,
allowing potentially invalid values such as NaN
, Infinity
, and
-Infinity
to be converted into Date
objects. During the encoding process,
any invalid Date
object will be encoded to NaN
.
DateFromNumber }), }, }), // Client documents can be used for local-only state (e.g. form inputs) uiState: State.SQLite.ClientDocumentTableDef<"uiState", { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, { partialSet: true; default: { id: typeof SessionIdSymbol; value: { readonly newTodoText: ""; readonly filter: "all"; }; };}>
uiState: import State
State.import SQLite
SQLite.clientDocument<"uiState", { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, { readonly name: "uiState"; readonly schema: Schema.Struct<{ newTodoText: typeof Schema.String; filter: Schema.Literal<["all", "active", "completed"]>; }>; readonly default: { readonly id: typeof SessionIdSymbol; readonly value: { readonly newTodoText: ""; readonly filter: "all"; }; };}>({ name, schema: valueSchema, ...inputOptions }: { name: "uiState"; schema: Schema.Schema<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }, { ...; }, never>;} & { readonly name: "uiState"; readonly schema: Schema.Struct<{ newTodoText: typeof Schema.String; filter: Schema.Literal<["all", "active", "completed"]>; }>; readonly default: { readonly id: typeof SessionIdSymbol; readonly value: { readonly newTodoText: ""; readonly filter: "all"; }; };}): State.SQLite.ClientDocumentTableDef<...>export clientDocument
Special:
- Synced across client sessions (e.g. tabs) but not across different clients
- Derived setters
- Emits client-only events
- Has implicit setter-materializers
- Similar to
React.useState
(except it's persisted)
Careful:
- When changing the table definitions in a non-backwards compatible way, the state might be lost without
explicit materializers to handle the old auto-generated events
Usage:
// Querying data// `'some-id'` can be ommited for SessionIdSymbolstore.queryDb(clientDocumentTable.get('some-id'))
// Setting data// Again, `'some-id'` can be ommited for SessionIdSymbolstore.commit(clientDocumentTable.set({ someField: 'some-value' }, 'some-id'))
clientDocument({ name: "uiState"
name: 'uiState', schema: Schema.Schema<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, never> & Schema.Struct<{ newTodoText: typeof Schema.String; filter: Schema.Literal<["all", "active", "completed"]>;}>
schema: import Schema
Schema.function Struct<{ newTodoText: typeof Schema.String; filter: Schema.Literal<["all", "active", "completed"]>;}>(fields: { newTodoText: typeof Schema.String; filter: Schema.Literal<["all", "active", "completed"]>;}): Schema.Struct<{ newTodoText: typeof Schema.String; filter: Schema.Literal<["all", "active", "completed"]>;}> (+1 overload)
Struct({ newTodoText: typeof Schema.String
newTodoText: import Schema
Schema.class Stringexport String
String, filter: Schema.Literal<["all", "active", "completed"]>
filter: import Schema
Schema.function Literal<["all", "active", "completed"]>(literals_0: "all", literals_1: "active", literals_2: "completed"): Schema.Literal<["all", "active", "completed"]> (+2 overloads)
Literal('all', 'active', 'completed') }), default: { readonly id: typeof SessionIdSymbol; readonly value: { readonly newTodoText: ""; readonly filter: "all"; };}
default: { id: typeof SessionIdSymbol
id: const SessionIdSymbol: typeof SessionIdSymbol
Can be used in queries to refer to the current session id.
Will be replaced with the actual session id at runtime
In client document table:
const uiState = State.SQLite.clientDocument({ name: 'ui_state', schema: Schema.Struct({ theme: Schema.Literal('dark', 'light', 'system'), user: Schema.String, showToolbar: Schema.Boolean, }), default: { value: defaultFrontendState, id: SessionIdSymbol },})
Or in a client document query:
const query$ = queryDb(tables.uiState.get(SessionIdSymbol))
SessionIdSymbol, value: { readonly newTodoText: ""; readonly filter: "all";}
value: { newTodoText: ""
newTodoText: '', filter: "all"
filter: 'all' } }, }),}
// Events describe data changes (https://docs.livestore.dev/reference/events)export const const events: { todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>; todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: State.SQLite.EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: State.SQLite.EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: State.SQLite.EventDef<...>; uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<...>;}
events = { todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string;}, { readonly text: string; readonly id: string;}, false>
todoCreated: import Events
Events.synced<"v1.TodoCreated", { readonly text: string; readonly id: string;}, { readonly text: string; readonly id: string;}>(args: { name: "v1.TodoCreated"; schema: Schema.Schema<{ readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, never>;} & Omit<State.SQLite.DefineEventOptions<{ readonly text: string; readonly id: string;}, false>, "derived" | "clientOnly">): State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string;}, { readonly text: string; readonly id: string;}, false>export synced
synced({ name: "v1.TodoCreated"
name: 'v1.TodoCreated', schema: Schema.Schema<{ readonly text: string; readonly id: string;}, { readonly text: string; readonly id: string;}, never>
schema: import Schema
Schema.function Struct<{ id: typeof Schema.String; text: typeof Schema.String;}>(fields: { id: typeof Schema.String; text: typeof Schema.String;}): Schema.Struct<{ id: typeof Schema.String; text: typeof Schema.String;}> (+1 overload)
Struct({ id: typeof Schema.String
id: import Schema
Schema.class Stringexport String
String, text: typeof Schema.String
text: import Schema
Schema.class Stringexport String
String }), }), todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", { readonly id: string;}, { readonly id: string;}, false>
todoCompleted: import Events
Events.synced<"v1.TodoCompleted", { readonly id: string;}, { readonly id: string;}>(args: { name: "v1.TodoCompleted"; schema: Schema.Schema<{ readonly id: string; }, { readonly id: string; }, never>;} & Omit<State.SQLite.DefineEventOptions<{ readonly id: string;}, false>, "derived" | "clientOnly">): State.SQLite.EventDef<"v1.TodoCompleted", { readonly id: string;}, { readonly id: string;}, false>export synced
synced({ name: "v1.TodoCompleted"
name: 'v1.TodoCompleted', schema: Schema.Schema<{ readonly id: string;}, { readonly id: string;}, never>
schema: import Schema
Schema.function Struct<{ id: typeof Schema.String;}>(fields: { id: typeof Schema.String;}): Schema.Struct<{ id: typeof Schema.String;}> (+1 overload)
Struct({ id: typeof Schema.String
id: import Schema
Schema.class Stringexport String
String }), }), todoUncompleted: State.SQLite.EventDef<"v1.TodoUncompleted", { readonly id: string;}, { readonly id: string;}, false>
todoUncompleted: import Events
Events.synced<"v1.TodoUncompleted", { readonly id: string;}, { readonly id: string;}>(args: { name: "v1.TodoUncompleted"; schema: Schema.Schema<{ readonly id: string; }, { readonly id: string; }, never>;} & Omit<State.SQLite.DefineEventOptions<{ readonly id: string;}, false>, "derived" | "clientOnly">): State.SQLite.EventDef<"v1.TodoUncompleted", { readonly id: string;}, { readonly id: string;}, false>export synced
synced({ name: "v1.TodoUncompleted"
name: 'v1.TodoUncompleted', schema: Schema.Schema<{ readonly id: string;}, { readonly id: string;}, never>
schema: import Schema
Schema.function Struct<{ id: typeof Schema.String;}>(fields: { id: typeof Schema.String;}): Schema.Struct<{ id: typeof Schema.String;}> (+1 overload)
Struct({ id: typeof Schema.String
id: import Schema
Schema.class Stringexport String
String }), }), todoDeleted: State.SQLite.EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date;}, { readonly id: string; readonly deletedAt: string;}, false>
todoDeleted: import Events
Events.synced<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date;}, { readonly id: string; readonly deletedAt: string;}>(args: { name: "v1.TodoDeleted"; schema: Schema.Schema<{ readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, never>;} & Omit<State.SQLite.DefineEventOptions<{ readonly id: string; readonly deletedAt: Date;}, false>, "derived" | "clientOnly">): State.SQLite.EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date;}, { readonly id: string; readonly deletedAt: string;}, false>export synced
synced({ name: "v1.TodoDeleted"
name: 'v1.TodoDeleted', schema: Schema.Schema<{ readonly id: string; readonly deletedAt: Date;}, { readonly id: string; readonly deletedAt: string;}, never>
schema: import Schema
Schema.function Struct<{ id: typeof Schema.String; deletedAt: typeof Schema.Date;}>(fields: { id: typeof Schema.String; deletedAt: typeof Schema.Date;}): Schema.Struct<{ id: typeof Schema.String; deletedAt: typeof Schema.Date;}> (+1 overload)
Struct({ id: typeof Schema.String
id: import Schema
Schema.class Stringexport String
String, deletedAt: typeof Schema.Date
deletedAt: import Schema
Schema.class Dateexport Date
This schema converts a string
into a Date
object using the new Date
constructor. It ensures that only valid date strings are accepted,
rejecting any strings that would result in an invalid date, such as new Date("Invalid Date")
.
Date }), }), todoClearedCompleted: State.SQLite.EventDef<"v1.TodoClearedCompleted", { readonly deletedAt: Date;}, { readonly deletedAt: string;}, false>
todoClearedCompleted: import Events
Events.synced<"v1.TodoClearedCompleted", { readonly deletedAt: Date;}, { readonly deletedAt: string;}>(args: { name: "v1.TodoClearedCompleted"; schema: Schema.Schema<{ readonly deletedAt: Date; }, { readonly deletedAt: string; }, never>;} & Omit<State.SQLite.DefineEventOptions<{ readonly deletedAt: Date;}, false>, "derived" | "clientOnly">): State.SQLite.EventDef<"v1.TodoClearedCompleted", { readonly deletedAt: Date;}, { readonly deletedAt: string;}, false>export synced
synced({ name: "v1.TodoClearedCompleted"
name: 'v1.TodoClearedCompleted', schema: Schema.Schema<{ readonly deletedAt: Date;}, { readonly deletedAt: string;}, never>
schema: import Schema
Schema.function Struct<{ deletedAt: typeof Schema.Date;}>(fields: { deletedAt: typeof Schema.Date;}): Schema.Struct<{ deletedAt: typeof Schema.Date;}> (+1 overload)
Struct({ deletedAt: typeof Schema.Date
deletedAt: import Schema
Schema.class Dateexport Date
This schema converts a string
into a Date
object using the new Date
constructor. It ensures that only valid date strings are accepted,
rejecting any strings that would result in an invalid date, such as new Date("Invalid Date")
.
Date }), }), uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<"uiState", { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, { partialSet: true; default: { id: typeof SessionIdSymbol; value: { readonly newTodoText: ""; readonly filter: "all"; }; };}>
uiStateSet: const tables: { todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; }; }>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>; uiState: State.SQLite.ClientDocumentTableDef<...>;}
tables.uiState: State.SQLite.ClientDocumentTableDef<"uiState", { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, { partialSet: true; default: { id: typeof SessionIdSymbol; value: { readonly newTodoText: ""; readonly filter: "all"; }; };}>
uiState.ClientDocumentTableDef<TName extends string, TType, TEncoded, TOptions extends ClientDocumentTableOptions<TType>>.Trait<"uiState", { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }, { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }, { ...; }>.set: State.SQLite.ClientDocumentTableDef.SetEventDefLike<"uiState", { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, { partialSet: true; default: { id: typeof SessionIdSymbol; value: { readonly newTodoText: ""; readonly filter: "all"; }; };}>
Derived event definition for setting the value of the client document table.
If the document doesn't exist yet, the first .set event will create it.
set,}
// Materializers are used to map events to state (https://docs.livestore.dev/reference/state/materializers)const const materializers: { "v1.TodoCreated": State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>>; "v1.TodoCompleted": State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>>; "v1.TodoUncompleted": State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>>; "v1.TodoDeleted": State.SQLite.Materializer<...>; "v1.TodoClearedCompleted": State.SQLite.Materializer<...>;}
materializers = import State
State.import SQLite
SQLite.const materializers: <{ todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>; todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: State.SQLite.EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: State.SQLite.EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: State.SQLite.EventDef<...>; uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<...>;}>(_eventDefRecord: { todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>; todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: State.SQLite.EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: State.SQLite.EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: State.SQLite.EventDef<...>; uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<...>;}, handlers: { ...;}) => { ...;}
materializers(const events: { todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>; todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: State.SQLite.EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: State.SQLite.EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: State.SQLite.EventDef<...>; uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<...>;}
events, { 'v1.TodoCreated': ({ id: string
id, text: string
text }) => const tables: { todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; }; }>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>; uiState: State.SQLite.ClientDocumentTableDef<...>;}
tables.todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; };}>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>
todos.insert: (values: { readonly id: string; readonly text?: string; readonly completed?: boolean; readonly deletedAt?: Date | null;}) => QueryBuilder<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { ...; }; readonly deletedAt: { ...; };}>, State.SQLite.WithDefaults<...>>, "select" | ... 6 more ... | "row">
Insert a new row into the table
Example:
db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' })
insert({ id: string
id, text?: string
text, completed?: boolean
completed: false }), 'v1.TodoCompleted': ({ id: string
id }) => const tables: { todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; }; }>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>; uiState: State.SQLite.ClientDocumentTableDef<...>;}
tables.todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; };}>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>
todos.update: (values: Partial<{ readonly text: string; readonly id: string; readonly completed: boolean; readonly deletedAt: Date | null;}>) => QueryBuilder<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { ...; }; readonly deletedAt: { ...; };}>, State.SQLite.WithDefaults<...>>, "select" | ... 6 more ... | "row">
Update rows in the table that match the where clause
Example:
db.todos.update({ status: 'completed' }).where({ id: '123' })
update({ completed?: boolean
completed: true }).where: (params: Partial<{ readonly id: string | { op: QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly text: string | { op: QueryBuilder.WhereOps.SingleValue; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly completed: boolean | ... 2 more ... | undefined; readonly deletedAt: Date | ... 3 more ... | undefined;}>) => QueryBuilder<...> (+2 overloads)
where({ id?: string | { op: QueryBuilder.WhereOps.SingleValue; value: string;} | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[];} | undefined
id }), 'v1.TodoUncompleted': ({ id: string
id }) => const tables: { todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; }; }>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>; uiState: State.SQLite.ClientDocumentTableDef<...>;}
tables.todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; };}>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>
todos.update: (values: Partial<{ readonly text: string; readonly id: string; readonly completed: boolean; readonly deletedAt: Date | null;}>) => QueryBuilder<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { ...; }; readonly deletedAt: { ...; };}>, State.SQLite.WithDefaults<...>>, "select" | ... 6 more ... | "row">
Update rows in the table that match the where clause
Example:
db.todos.update({ status: 'completed' }).where({ id: '123' })
update({ completed?: boolean
completed: false }).where: (params: Partial<{ readonly id: string | { op: QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly text: string | { op: QueryBuilder.WhereOps.SingleValue; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly completed: boolean | ... 2 more ... | undefined; readonly deletedAt: Date | ... 3 more ... | undefined;}>) => QueryBuilder<...> (+2 overloads)
where({ id?: string | { op: QueryBuilder.WhereOps.SingleValue; value: string;} | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[];} | undefined
id }), 'v1.TodoDeleted': ({ id: string
id, deletedAt: Date
deletedAt }) => const tables: { todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; }; }>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>; uiState: State.SQLite.ClientDocumentTableDef<...>;}
tables.todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; };}>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>
todos.update: (values: Partial<{ readonly text: string; readonly id: string; readonly completed: boolean; readonly deletedAt: Date | null;}>) => QueryBuilder<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { ...; }; readonly deletedAt: { ...; };}>, State.SQLite.WithDefaults<...>>, "select" | ... 6 more ... | "row">
Update rows in the table that match the where clause
Example:
db.todos.update({ status: 'completed' }).where({ id: '123' })
update({ deletedAt?: Date | null
deletedAt }).where: (params: Partial<{ readonly id: string | { op: QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly text: string | { op: QueryBuilder.WhereOps.SingleValue; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly completed: boolean | ... 2 more ... | undefined; readonly deletedAt: Date | ... 3 more ... | undefined;}>) => QueryBuilder<...> (+2 overloads)
where({ id?: string | { op: QueryBuilder.WhereOps.SingleValue; value: string;} | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[];} | undefined
id }), 'v1.TodoClearedCompleted': ({ deletedAt: Date
deletedAt }) => const tables: { todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; }; }>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>; uiState: State.SQLite.ClientDocumentTableDef<...>;}
tables.todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; };}>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>
todos.update: (values: Partial<{ readonly text: string; readonly id: string; readonly completed: boolean; readonly deletedAt: Date | null;}>) => QueryBuilder<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { ...; }; readonly deletedAt: { ...; };}>, State.SQLite.WithDefaults<...>>, "select" | ... 6 more ... | "row">
Update rows in the table that match the where clause
Example:
db.todos.update({ status: 'completed' }).where({ id: '123' })
update({ deletedAt?: Date | null
deletedAt }).where: (params: Partial<{ readonly id: string | { op: QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly text: string | { op: QueryBuilder.WhereOps.SingleValue; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly completed: boolean | ... 2 more ... | undefined; readonly deletedAt: Date | ... 3 more ... | undefined;}>) => QueryBuilder<...> (+2 overloads)
where({ completed?: boolean | { op: QueryBuilder.WhereOps.SingleValue; value: boolean;} | { op: QueryBuilder.WhereOps.MultiValue; value: readonly boolean[];} | undefined
completed: true }),})
const const state: InternalState
state = import State
State.import SQLite
SQLite.const makeState: <{ tables: { todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; }; }>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>; uiState: State.SQLite.ClientDocumentTableDef<...>; }; materializers: { ...; };}>(inputSchema: { tables: { todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; }; }>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>; uiState: State.SQLite.ClientDocumentTableDef<...>; }; materializers: { ...; };}) => InternalState
makeState({ tables: { todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { ...; }; }>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>; uiState: State.SQLite.ClientDocumentTableDef<...>;}
tables, materializers: { "v1.TodoCreated": State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>>; "v1.TodoCompleted": State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>>; "v1.TodoUncompleted": State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>>; "v1.TodoDeleted": State.SQLite.Materializer<...>; "v1.TodoClearedCompleted": State.SQLite.Materializer<...>;}
materializers })
export const const schema: FromInputSchema.DeriveSchema<{ events: { todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>; todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: State.SQLite.EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: State.SQLite.EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: State.SQLite.EventDef<...>; uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<...>; }; state: InternalState;}>
schema = makeSchema<{ events: { todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>; todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: State.SQLite.EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: State.SQLite.EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: State.SQLite.EventDef<...>; uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<...>; }; state: InternalState;}>(inputSchema: { events: { todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>; todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: State.SQLite.EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: State.SQLite.EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: State.SQLite.EventDef<...>; uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<...>; }; state: InternalState;}): FromInputSchema.DeriveSchema<...>
makeSchema({ events: { todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>; todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: State.SQLite.EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: State.SQLite.EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: State.SQLite.EventDef<...>; uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<...>;}
events, state: InternalState
state })
Create the LiveStore Worker
Section titled “Create the LiveStore Worker”Create a file named livestore.worker.ts
inside the src
folder. This file will contain the LiveStore web worker. When importing this file, make sure to add the ?worker
extension to the import path to ensure that Vite treats it as a worker file.
import { const makeWorker: (options: WorkerOptions) => void
makeWorker } from '@livestore/adapter-web/worker'
import { const schema: FromInputSchema.DeriveSchema<{ events: { todoCreated: EventDef<"v1.TodoCreated", { readonly id: string; readonly text: string; }, { readonly id: string; readonly text: string; }, false>; todoCompleted: EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: EventDef<...>; uiStateSet: ClientDocumentTableDef.SetEventDefLike<...>; }; state: InternalState;}>
schema } from './livestore/schema.ts'
function makeWorker(options: WorkerOptions): void
makeWorker({ schema: LiveStoreSchema<DbSchema, EventDefRecord>
schema })
Add the LiveStore Provider
Section titled “Add the LiveStore Provider”To make the LiveStore available throughout your app, wrap your app’s root component with the LiveStoreProvider
component from @livestore/react
. This provider manages your app’s data store, loading, and error states.
Here’s an example:
/// <reference types="vite/client" />
import { const makePersistedAdapter: (options: WebAdapterOptions) => Adapter
Creates a web adapter with persistent storage (currently only supports OPFS).
Requires both a web worker and a shared worker.
makePersistedAdapter } from '@livestore/adapter-web'import const LiveStoreSharedWorker: new (options?: { name?: string;}) => SharedWorker
LiveStoreSharedWorker from '@livestore/adapter-web/shared-worker?sharedworker'import { const LiveStoreProvider: ({ renderLoading, renderError, renderShutdown, otelOptions, children, schema, storeId, boot, adapter, batchUpdates, disableDevtools, signal, confirmUnsavedChanges, syncPayload, debug, }: LiveStoreProviderProps & React.PropsWithChildren) => React.ReactNode
LiveStoreProvider } from '@livestore/react'import type (alias) namespace Reactimport React
React from 'react'import { function unstable_batchedUpdates<A, R>(callback: (a: A) => R, a: A): R (+1 overload)
unstable_batchedUpdates as function batchUpdates<A, R>(callback: (a: A) => R, a: A): R (+1 overload)
batchUpdates } from 'react-dom'import { const schema: FromInputSchema.DeriveSchema<{ events: { todoCreated: EventDef<"v1.TodoCreated", { readonly id: string; readonly text: string; }, { readonly id: string; readonly text: string; }, false>; todoCompleted: EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: EventDef<...>; uiStateSet: ClientDocumentTableDef.SetEventDefLike<...>; }; state: InternalState;}>
schema } from './livestore/schema.ts'import const LiveStoreWorker: new (options?: { name?: string;}) => Worker
LiveStoreWorker from './livestore.worker.ts?worker'
const const adapter: Adapter
adapter = function makePersistedAdapter(options: WebAdapterOptions): Adapter
Creates a web adapter with persistent storage (currently only supports OPFS).
Requires both a web worker and a shared worker.
makePersistedAdapter({ storage: { readonly type: "opfs"; readonly directory?: string | undefined;}
Specifies where to persist data for this adapter
storage: { type: "opfs"
type: 'opfs' }, worker: ((options: { name: string;}) => globalThis.Worker) | (new (options: { name: string;}) => globalThis.Worker)
worker: const LiveStoreWorker: new (options?: { name?: string;}) => Worker
LiveStoreWorker, sharedWorker: ((options: { name: string;}) => globalThis.SharedWorker) | (new (options: { name: string;}) => globalThis.SharedWorker)
This is mostly an implementation detail and needed to be exposed into app code
due to a current Vite limitation (https://github.com/vitejs/vite/issues/8427).
In most cases this should look like:
import LiveStoreSharedWorker from '@livestore/adapter-web/shared-worker?sharedworker'
const adapter = makePersistedAdapter({ sharedWorker: LiveStoreSharedWorker, // ...})
sharedWorker: const LiveStoreSharedWorker: new (options?: { name?: string;}) => SharedWorker
LiveStoreSharedWorker,})
export const const App: React.FC<{}>
App: (alias) namespace Reactimport React
React.type React.FC<P = {}> = React.FunctionComponent<P>
Represents the type of a function component. Can optionally
receive a type argument that represents the props the component
receives.
FC = () => ( <const LiveStoreProvider: ({ renderLoading, renderError, renderShutdown, otelOptions, children, schema, storeId, boot, adapter, batchUpdates, disableDevtools, signal, confirmUnsavedChanges, syncPayload, debug, }: LiveStoreProviderProps & React.PropsWithChildren) => React.ReactNode
LiveStoreProvider LiveStoreProviderProps.schema: LiveStoreSchema<DbSchema, EventDefRecord>
schema={const schema: FromInputSchema.DeriveSchema<{ events: { todoCreated: EventDef<"v1.TodoCreated", { readonly id: string; readonly text: string; }, { readonly id: string; readonly text: string; }, false>; todoCompleted: EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: EventDef<...>; uiStateSet: ClientDocumentTableDef.SetEventDefLike<...>; }; state: InternalState;}>
schema} LiveStoreProviderProps.adapter: Adapter
adapter={const adapter: Adapter
adapter} LiveStoreProviderProps.renderLoading?: (status: BootStatus) => React.ReactNode
renderLoading={(_: { readonly stage: "loading";} | { readonly stage: "migrating"; readonly progress: { readonly done: number; readonly total: number; };} | { readonly stage: "rehydrating"; readonly progress: { readonly done: number; readonly total: number; };} | { readonly stage: "syncing"; readonly progress: { readonly done: number; readonly total: number; };} | { readonly stage: "done";}
_) => <React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div>Loading LiveStore ({_: { readonly stage: "loading";} | { readonly stage: "migrating"; readonly progress: { readonly done: number; readonly total: number; };} | { readonly stage: "rehydrating"; readonly progress: { readonly done: number; readonly total: number; };} | { readonly stage: "syncing"; readonly progress: { readonly done: number; readonly total: number; };} | { readonly stage: "done";}
_.stage: "loading" | "migrating" | "done" | "rehydrating" | "syncing"
stage})...</React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div>} LiveStoreProviderProps.batchUpdates: (run: () => void) => void
In order for LiveStore to apply multiple events in a single render,
you need to pass the batchUpdates
function from either react-dom
or react-native
.
// With React DOMimport { unstable_batchedUpdates as batchUpdates } from 'react-dom'
// With React Nativeimport { unstable_batchedUpdates as batchUpdates } from 'react-native'
batchUpdates={function batchUpdates<A, R>(callback: (a: A) => R, a: A): R (+1 overload)
batchUpdates} LiveStoreProviderProps.storeId?: string
The storeId
can be used to isolate multiple stores from each other.
So it can be useful for multi-tenancy scenarios.
The storeId
is also used for persistence.
Make sure to also configure storeId
in LiveStore Devtools (e.g. in Vite plugin).
storeId="my-app-store" > <React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div React.HTMLAttributes<HTMLDivElement>.className?: string | undefined
className="todoapp">{/* Your app components go here */}</React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div> </const LiveStoreProvider: ({ renderLoading, renderError, renderShutdown, otelOptions, children, schema, storeId, boot, adapter, batchUpdates, disableDevtools, signal, confirmUnsavedChanges, syncPayload, debug, }: LiveStoreProviderProps & React.PropsWithChildren) => React.ReactNode
LiveStoreProvider>)
Commit events
Section titled “Commit events”After wrapping your app with the LiveStoreProvider
, you can use the useStore
hook from any component to commit events.
Here’s an example:
import { const queryDb: { <TResultSchema, TResult = TResultSchema>(queryInput: QueryInputRaw<TResultSchema, ReadonlyArray<any>> | QueryBuilder<TResultSchema, any, any>, options?: { map?: (rows: TResultSchema) => TResult; label?: string; deps?: DepKey; }): LiveQueryDef<TResult>; <TResultSchema, TResult = TResultSchema>(queryInput: ((get: GetAtomResult) => QueryInputRaw<TResultSchema, ReadonlyArray<any>>) | ((get: GetAtomResult) => QueryBuilder<TResultSchema, any, any>), options?: { map?: (rows: TResultSchema) => TResult; label?: string; deps?: DepKey; }): LiveQueryDef<TResult>;}
NOTE queryDb
is only supposed to read data. Don't use it to insert/update/delete data but use events instead.
When using contextual data when constructing the query, please make sure to include it in the deps
option.
queryDb } from '@livestore/livestore'import { const useStore: (options?: { store?: Store;}) => { store: Store & ReactApi;}
useStore } from '@livestore/react'import type (alias) namespace Reactimport React
React from 'react'
import { const events: { todoCreated: EventDef<"v1.TodoCreated", { readonly id: string; readonly text: string; }, { readonly id: string; readonly text: string; }, false>; todoCompleted: EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: EventDef<...>; uiStateSet: ClientDocumentTableDef.SetEventDefLike<...>;}
events, const tables: { todos: TableDef<SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { columnType: "integer"; ... 4 more ...; autoIncrement: false; }; }>, WithDefaults<...>, Schema<...>>; uiState: ClientDocumentTableDef<...>;}
tables } from './livestore/schema.ts'
const const uiState$: LiveQueryDef<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, "def">
uiState$ = queryDb<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}>(queryInput: QueryInputRaw<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, readonly any[]> | QueryBuilder<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, any, any>, options?: { map?: (rows: { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }) => { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }; label?: string; deps?: DepKey;} | undefined): LiveQueryDef<...> (+1 overload)
NOTE queryDb
is only supposed to read data. Don't use it to insert/update/delete data but use events instead.
When using contextual data when constructing the query, please make sure to include it in the deps
option.
queryDb(const tables: { todos: TableDef<SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { columnType: "integer"; ... 4 more ...; autoIncrement: false; }; }>, WithDefaults<...>, Schema<...>>; uiState: ClientDocumentTableDef<...>;}
tables.uiState: ClientDocumentTableDef<"uiState", { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, { partialSet: true; default: { id: typeof SessionIdSymbol; value: { readonly newTodoText: ""; readonly filter: "all"; }; };}>
uiState.ClientDocumentTableDef<TName extends string, TType, TEncoded, TOptions extends ClientDocumentTableOptions<TType>>.Trait<"uiState", { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }, { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }, { ...; }>.get: (id?: typeof SessionIdSymbol | undefined, options?: { default: Partial<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }>;} | undefined) => QueryBuilder<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, ClientDocumentTableDef<TName extends string, TType, TEncoded, TOptions extends ClientDocumentTableOptions<TType>>.TableDefBase_<"uiState", { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}>, QueryBuilder<TResult, TTableDef extends TableDefBase, TWithout extends QueryBuilder.ApiFeature = never>.ApiFeature>
Get the current value of the client document table.
get(), { label?: string
Used for debugging / devtools
label: 'uiState' })
export const const Header: React.FC<{}>
Header: (alias) namespace Reactimport React
React.type React.FC<P = {}> = React.FunctionComponent<P>
Represents the type of a function component. Can optionally
receive a type argument that represents the props the component
receives.
FC = () => { const { const store: Store<LiveStoreSchema.Any, {}> & ReactApi
store } = function useStore(options?: { store?: Store;}): { store: Store & ReactApi;}
useStore() const { const newTodoText: string
newTodoText } = const store: Store<LiveStoreSchema.Any, {}> & ReactApi
store.useQuery: <LiveQueryDef<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, "def">>(queryDef: LiveQueryDef<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, "def">, options?: { store?: Store;}) => { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}
Returns the result of a query and subscribes to future updates.
Example:
const App = () => { const todos = useQuery(queryDb(tables.todos.query.where({ complete: true }))) return <div>{todos.map((todo) => <div key={todo.id}>{todo.title}</div>)}</div>}
useQuery(const uiState$: LiveQueryDef<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, "def">
uiState$)
const const updateNewTodoText: (text: string) => void
updateNewTodoText = (text: string
text: string) => const store: Store<LiveStoreSchema.Any, {}> & ReactApi
store.Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.commit: <readonly [{ name: "uiStateSet"; args: { id: string; value: { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }; };}]>(list_0: { name: "uiStateSet"; args: { id: string; value: { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }; };}) => void (+3 overloads)
commit(const events: { todoCreated: EventDef<"v1.TodoCreated", { readonly id: string; readonly text: string; }, { readonly id: string; readonly text: string; }, false>; todoCompleted: EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: EventDef<...>; uiStateSet: ClientDocumentTableDef.SetEventDefLike<...>;}
events.uiStateSet: (args: Partial<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}>, id?: string | SessionIdSymbol) => { name: "uiStateSet"; args: { id: string; value: { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }; };}
uiStateSet({ newTodoText?: string
newTodoText: text: string
text }))
const const createTodo: () => void
createTodo = () => const store: Store<LiveStoreSchema.Any, {}> & ReactApi
store.Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.commit: <readonly [{ name: "v1.TodoCreated"; args: { readonly id: string; readonly text: string; };}, { name: "uiStateSet"; args: { id: string; value: { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }; };}]>(list_0: { name: "v1.TodoCreated"; args: { readonly id: string; readonly text: string; };}, list_1: { name: "uiStateSet"; args: { id: string; value: { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }; };}) => void (+3 overloads)
commit( const events: { todoCreated: EventDef<"v1.TodoCreated", { readonly id: string; readonly text: string; }, { readonly id: string; readonly text: string; }, false>; todoCompleted: EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: EventDef<...>; uiStateSet: ClientDocumentTableDef.SetEventDefLike<...>;}
events.todoCreated: (args: { readonly id: string; readonly text: string;}) => { name: "v1.TodoCreated"; args: { readonly id: string; readonly text: string; };}
Helper function to construct a partial event
todoCreated({ id: string
id: var crypto: Crypto
crypto.Crypto.randomUUID(): `${string}-${string}-${string}-${string}-${string}` (+1 overload)
randomUUID(), text: string
text: const newTodoText: string
newTodoText }), const events: { todoCreated: EventDef<"v1.TodoCreated", { readonly id: string; readonly text: string; }, { readonly id: string; readonly text: string; }, false>; todoCompleted: EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: EventDef<...>; uiStateSet: ClientDocumentTableDef.SetEventDefLike<...>;}
events.uiStateSet: (args: Partial<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}>, id?: string | SessionIdSymbol) => { name: "uiStateSet"; args: { id: string; value: { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }; };}
uiStateSet({ newTodoText?: string
newTodoText: '' }), )
return ( <React.JSX.IntrinsicElements.header: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>
header React.HTMLAttributes<HTMLElement>.className?: string | undefined
className="header"> <React.JSX.IntrinsicElements.h1: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
h1>TodoMVC</React.JSX.IntrinsicElements.h1: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
h1> <React.JSX.IntrinsicElements.input: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>
input React.HTMLAttributes<T>.className?: string | undefined
className="new-todo" React.InputHTMLAttributes<HTMLInputElement>.placeholder?: string | undefined
placeholder="What needs to be done?" React.InputHTMLAttributes<HTMLInputElement>.value?: string | number | readonly string[] | undefined
value={const newTodoText: string
newTodoText} React.InputHTMLAttributes<HTMLInputElement>.onChange?: React.ChangeEventHandler<HTMLInputElement> | undefined
onChange={(e: React.ChangeEvent<HTMLInputElement>
e) => const updateNewTodoText: (text: string) => void
updateNewTodoText(e: React.ChangeEvent<HTMLInputElement>
e.React.ChangeEvent<HTMLInputElement>.target: EventTarget & HTMLInputElement
target.HTMLInputElement.value: string
The value
property of the HTMLInputElement interface represents the current value of the input element as a string.
value)} React.DOMAttributes<HTMLInputElement>.onKeyDown?: React.KeyboardEventHandler<HTMLInputElement> | undefined
onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>
e) => { if (e: React.KeyboardEvent<HTMLInputElement>
e.React.KeyboardEvent<HTMLInputElement>.key: string
See the DOM Level 3 Events spec. for possible values
key === 'Enter') { const createTodo: () => void
createTodo() } }} /> </React.JSX.IntrinsicElements.header: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>
header> )}
Queries
Section titled “Queries”To retrieve data from the database, first define a query using queryDb
from @livestore/livestore
. Then, execute the query with the useQuery
hook from @livestore/react
.
Consider abstracting queries into a separate file to keep your code organized, though you can also define them directly within components if preferred.
Here’s an example:
import { const queryDb: { <TResultSchema, TResult = TResultSchema>(queryInput: QueryInputRaw<TResultSchema, ReadonlyArray<any>> | QueryBuilder<TResultSchema, any, any>, options?: { map?: (rows: TResultSchema) => TResult; label?: string; deps?: DepKey; }): LiveQueryDef<TResult>; <TResultSchema, TResult = TResultSchema>(queryInput: ((get: GetAtomResult) => QueryInputRaw<TResultSchema, ReadonlyArray<any>>) | ((get: GetAtomResult) => QueryBuilder<TResultSchema, any, any>), options?: { map?: (rows: TResultSchema) => TResult; label?: string; deps?: DepKey; }): LiveQueryDef<TResult>;}
NOTE queryDb
is only supposed to read data. Don't use it to insert/update/delete data but use events instead.
When using contextual data when constructing the query, please make sure to include it in the deps
option.
queryDb } from '@livestore/livestore'import { const useStore: (options?: { store?: Store;}) => { store: Store & ReactApi;}
useStore } from '@livestore/react'import (alias) namespace Reactimport React
React from 'react'
import { const events: { todoCreated: EventDef<"v1.TodoCreated", { readonly id: string; readonly text: string; }, { readonly id: string; readonly text: string; }, false>; todoCompleted: EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: EventDef<...>; uiStateSet: ClientDocumentTableDef.SetEventDefLike<...>;}
events, const tables: { todos: TableDef<SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { columnType: "integer"; ... 4 more ...; autoIncrement: false; }; }>, WithDefaults<...>, Schema<...>>; uiState: ClientDocumentTableDef<...>;}
tables } from './livestore/schema.ts'
const const uiState$: LiveQueryDef<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, "def">
uiState$ = queryDb<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}>(queryInput: QueryInputRaw<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, readonly any[]> | QueryBuilder<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, any, any>, options?: { map?: (rows: { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }) => { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }; label?: string; deps?: DepKey;} | undefined): LiveQueryDef<...> (+1 overload)
NOTE queryDb
is only supposed to read data. Don't use it to insert/update/delete data but use events instead.
When using contextual data when constructing the query, please make sure to include it in the deps
option.
queryDb(const tables: { todos: TableDef<SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { columnType: "integer"; ... 4 more ...; autoIncrement: false; }; }>, WithDefaults<...>, Schema<...>>; uiState: ClientDocumentTableDef<...>;}
tables.uiState: ClientDocumentTableDef<"uiState", { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, { partialSet: true; default: { id: typeof SessionIdSymbol; value: { readonly newTodoText: ""; readonly filter: "all"; }; };}>
uiState.ClientDocumentTableDef<TName extends string, TType, TEncoded, TOptions extends ClientDocumentTableOptions<TType>>.Trait<"uiState", { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }, { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }, { ...; }>.get: (id?: typeof SessionIdSymbol | undefined, options?: { default: Partial<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string; }>;} | undefined) => QueryBuilder<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, ClientDocumentTableDef<TName extends string, TType, TEncoded, TOptions extends ClientDocumentTableOptions<TType>>.TableDefBase_<"uiState", { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}>, QueryBuilder<TResult, TTableDef extends TableDefBase, TWithout extends QueryBuilder.ApiFeature = never>.ApiFeature>
Get the current value of the client document table.
get(), { label?: string
Used for debugging / devtools
label: 'uiState' })
const const visibleTodos$: LiveQueryDef<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[], "def">
visibleTodos$ = queryDb<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[], readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[]>(queryInput: ((get: GetAtomResult) => QueryInputRaw<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[], readonly any[]>) | ((get: GetAtomResult) => QueryBuilder<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[], any, any>), options?: { ...;} | undefined): LiveQueryDef<...> (+1 overload)
NOTE queryDb
is only supposed to read data. Don't use it to insert/update/delete data but use events instead.
When using contextual data when constructing the query, please make sure to include it in the deps
option.
queryDb( (get: GetAtomResult
get) => { const { const filter: "completed" | "all" | "active"
filter } = get: <{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}>(atom: LiveQueryDef<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, "def"> | LiveQuery<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}> | ISignal<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}> | SignalDef<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}> | Atom<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, any, RefreshReason>, otelContext?: Context | undefined, debugRefreshReason?: RefreshReason | undefined) => { readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}
get(const uiState$: LiveQueryDef<{ readonly filter: "completed" | "all" | "active"; readonly newTodoText: string;}, "def">
uiState$) return const tables: { todos: TableDef<SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { columnType: "integer"; ... 4 more ...; autoIncrement: false; }; }>, WithDefaults<...>, Schema<...>>; uiState: ClientDocumentTableDef<...>;}
tables.todos: TableDef<SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { columnType: "integer"; ... 4 more ...; autoIncrement: false; };}>, WithDefaults<...>, Schema<...>>
todos.where: (params: Partial<{ readonly id: string | { op: QueryBuilder<TResult, TTableDef extends TableDefBase, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly text: string | { op: QueryBuilder.WhereOps.SingleValue; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly completed: boolean | ... 2 more ... | undefined; readonly deletedAt: Date | ... 3 more ... | undefined;}>) => QueryBuilder<...> (+2 overloads)
where({ deletedAt?: Date | { op: QueryBuilder.WhereOps.SingleValue; value: Date | null;} | { op: QueryBuilder.WhereOps.MultiValue; value: readonly (Date | null)[];} | null | undefined
deletedAt: null, completed?: boolean | { op: QueryBuilder.WhereOps.SingleValue; value: boolean;} | { op: QueryBuilder.WhereOps.MultiValue; value: readonly boolean[];} | undefined
completed: const filter: "completed" | "all" | "active"
filter === 'all' ? var undefined
undefined : const filter: "completed" | "active"
filter === 'completed', }) }, { label?: string
Used for debugging / devtools
label: 'visibleTodos' },)
export const const MainSection: React.FC<{}>
MainSection: (alias) namespace Reactimport React
React.type React.FC<P = {}> = React.FunctionComponent<P>
Represents the type of a function component. Can optionally
receive a type argument that represents the props the component
receives.
FC = () => { const { const store: Store<LiveStoreSchema.Any, {}> & ReactApi
store } = function useStore(options?: { store?: Store;}): { store: Store & ReactApi;}
useStore()
const const toggleTodo: ({ id, completed }: typeof tables.todos.Type) => void
toggleTodo = (alias) namespace Reactimport React
React.function React.useCallback<({ id, completed }: typeof tables.todos.Type) => void>(callback: ({ id, completed }: typeof tables.todos.Type) => void, deps: React.DependencyList): ({ id, completed }: typeof tables.todos.Type) => void
useCallback
will return a memoized version of the callback that only changes if one of the inputs
has changed.
useCallback( ({ id: string
id, completed: boolean
completed }: typeof const tables: { todos: TableDef<SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { columnType: "integer"; ... 4 more ...; autoIncrement: false; }; }>, WithDefaults<...>, Schema<...>>; uiState: ClientDocumentTableDef<...>;}
tables.todos: TableDef<SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: Some<"">; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly deletedAt: { columnType: "integer"; ... 4 more ...; autoIncrement: false; };}>, WithDefaults<...>, Schema<...>>
todos.type Type: { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}
Type) => const store: Store<LiveStoreSchema.Any, {}> & ReactApi
store.Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.commit: <readonly [{ name: "v1.TodoUncompleted"; args: { readonly id: string; };} | { name: "v1.TodoCompleted"; args: { readonly id: string; };}]>(list_0: { name: "v1.TodoUncompleted"; args: { readonly id: string; };} | { name: "v1.TodoCompleted"; args: { readonly id: string; };}) => void (+3 overloads)
commit(completed: boolean
completed ? const events: { todoCreated: EventDef<"v1.TodoCreated", { readonly id: string; readonly text: string; }, { readonly id: string; readonly text: string; }, false>; todoCompleted: EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: EventDef<...>; uiStateSet: ClientDocumentTableDef.SetEventDefLike<...>;}
events.todoUncompleted: (args: { readonly id: string;}) => { name: "v1.TodoUncompleted"; args: { readonly id: string; };}
Helper function to construct a partial event
todoUncompleted({ id: string
id }) : const events: { todoCreated: EventDef<"v1.TodoCreated", { readonly id: string; readonly text: string; }, { readonly id: string; readonly text: string; }, false>; todoCompleted: EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: EventDef<...>; uiStateSet: ClientDocumentTableDef.SetEventDefLike<...>;}
events.todoCompleted: (args: { readonly id: string;}) => { name: "v1.TodoCompleted"; args: { readonly id: string; };}
Helper function to construct a partial event
todoCompleted({ id: string
id })), [const store: Store<LiveStoreSchema.Any, {}> & ReactApi
store], )
const const visibleTodos: readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[]
visibleTodos = const store: Store<LiveStoreSchema.Any, {}> & ReactApi
store.useQuery: <LiveQueryDef<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[], "def">>(queryDef: LiveQueryDef<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[], "def">, options?: { store?: Store;}) => readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[]
Returns the result of a query and subscribes to future updates.
Example:
const App = () => { const todos = useQuery(queryDb(tables.todos.query.where({ complete: true }))) return <div>{todos.map((todo) => <div key={todo.id}>{todo.title}</div>)}</div>}
useQuery(const visibleTodos$: LiveQueryDef<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[], "def">
visibleTodos$)
return ( <React.JSX.IntrinsicElements.section: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>
section React.HTMLAttributes<HTMLElement>.className?: string | undefined
className="main"> <React.JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>
ul React.HTMLAttributes<T>.className?: string | undefined
className="todo-list"> {const visibleTodos: readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[]
visibleTodos.ReadonlyArray<{ readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null; }>.map<JSX.Element>(callbackfn: (value: { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}, index: number, array: readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}[]) => JSX.Element, thisArg?: any): JSX.Element[]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
map((todo: { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}
todo) => ( <React.JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>
li React.Attributes.key?: React.Key | null | undefined
key={todo: { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}
todo.id: string
id}> <React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div React.HTMLAttributes<T>.className?: string | undefined
className="view"> <React.JSX.IntrinsicElements.input: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>
input React.InputHTMLAttributes<HTMLInputElement>.type?: React.HTMLInputTypeAttribute | undefined
type="checkbox" React.HTMLAttributes<T>.className?: string | undefined
className="toggle" React.HTMLAttributes<HTMLInputElement>.id?: string | undefined
id={`todo-${todo: { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}
todo.id: string
id}`} React.InputHTMLAttributes<HTMLInputElement>.checked?: boolean | undefined
checked={todo: { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}
todo.completed: boolean
completed} React.InputHTMLAttributes<HTMLInputElement>.onChange?: React.ChangeEventHandler<HTMLInputElement> | undefined
onChange={() => const toggleTodo: ({ id, completed }: typeof tables.todos.Type) => void
toggleTodo(todo: { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}
todo)} /> <React.JSX.IntrinsicElements.label: React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>
label React.LabelHTMLAttributes<HTMLLabelElement>.htmlFor?: string | undefined
htmlFor={`todo-${todo: { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}
todo.id: string
id}`}>{todo: { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}
todo.text: string
text}</React.JSX.IntrinsicElements.label: React.DetailedHTMLProps<React.LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>
label> <React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button React.ButtonHTMLAttributes<HTMLButtonElement>.type?: "button" | "reset" | "submit" | undefined
type="button" React.HTMLAttributes<T>.className?: string | undefined
className="destroy" React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
onClick={() => const store: Store<LiveStoreSchema.Any, {}> & ReactApi
store.Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.commit: <readonly [{ name: "v1.TodoDeleted"; args: { readonly id: string; readonly deletedAt: Date; };}]>(list_0: { name: "v1.TodoDeleted"; args: { readonly id: string; readonly deletedAt: Date; };}) => void (+3 overloads)
commit(const events: { todoCreated: EventDef<"v1.TodoCreated", { readonly id: string; readonly text: string; }, { readonly id: string; readonly text: string; }, false>; todoCompleted: EventDef<"v1.TodoCompleted", { readonly id: string; }, { readonly id: string; }, false>; todoUncompleted: EventDef<"v1.TodoUncompleted", { readonly id: string; }, { readonly id: string; }, false>; todoDeleted: EventDef<"v1.TodoDeleted", { readonly id: string; readonly deletedAt: Date; }, { readonly id: string; readonly deletedAt: string; }, false>; todoClearedCompleted: EventDef<...>; uiStateSet: ClientDocumentTableDef.SetEventDefLike<...>;}
events.todoDeleted: (args: { readonly id: string; readonly deletedAt: Date;}) => { name: "v1.TodoDeleted"; args: { readonly id: string; readonly deletedAt: Date; };}
Helper function to construct a partial event
todoDeleted({ id: string
id: todo: { readonly id: string; readonly text: string; readonly completed: boolean; readonly deletedAt: Date | null;}
todo.id: string
id, deletedAt: Date
deletedAt: new var Date: DateConstructornew () => Date (+3 overloads)
Date() }))} /> </React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div> </React.JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>
li> ))} </React.JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>
ul> </React.JSX.IntrinsicElements.section: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>
section> )}