Skip to content

SQLite State Schema

LiveStore provides a schema definition language for defining your database tables and mutation definitions using explicit column configurations. LiveStore automatically migrates your database schema when you change your schema definitions.

Alternative Approach: You can also define tables using Effect Schema with annotations for type-safe schema definitions.

import {
import Events
Events
,
const makeSchema: <TInputSchema extends InputSchema>(inputSchema: TInputSchema) => FromInputSchema.DeriveSchema<TInputSchema>
makeSchema
,
import Schema
Schema
,
type SessionIdSymbol = typeof SessionIdSymbol
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
,
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:

  1. Using explicit column definitions
  2. Using an Effect Schema (either the name property needs to be provided or the schema needs to have a title/identifier)
// Using explicit columns
const 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 annotations
import { 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 name
const 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 indexes
const 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.

@since3.10.0

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 SessionIdSymbol
store.queryDb(clientDocumentTable.get('some-id'))
// Setting data
// Again, `'some-id'` can be ommited for SessionIdSymbol
store.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)

@since3.10.0

Struct
({
newTodoText: typeof Schema.String
newTodoText
:
import Schema
Schema
.
class String
export String

@since3.10.0

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)

@since3.10.0

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)

@since3.10.0

Struct
({
id: typeof Schema.String
id
:
import Schema
Schema
.
class String
export String

@since3.10.0

String
,
text: typeof Schema.String
text
:
import Schema
Schema
.
class String
export String

@since3.10.0

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)

@since3.10.0

Struct
({
id: typeof Schema.String
id
:
import Schema
Schema
.
class String
export String

@since3.10.0

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)

@since3.10.0

Struct
({
id: typeof Schema.String
id
:
import Schema
Schema
.
class String
export String

@since3.10.0

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)

@since3.10.0

Struct
({
id: typeof Schema.String
id
:
import Schema
Schema
.
class String
export String

@since3.10.0

String
,
deletedAt: typeof Schema.Date
deletedAt
:
import Schema
Schema
.
class Date
export 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").

@since3.10.0

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)

@since3.10.0

Struct
({
deletedAt: typeof Schema.Date
deletedAt
:
import Schema
Schema
.
class Date
export 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").

@since3.10.0

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.

@example

const someDocumentTable = State.SQLite.clientDocument({
name: 'SomeDocumentTable',
schema: Schema.Struct({
someField: Schema.String,
someOtherField: Schema.String,
}),
default: { value: { someField: 'some-default-value', someOtherField: 'some-other-default-value' } },
})
const setEventDef = store.commit(someDocumentTable.set({ someField: 'explicit-value' }, 'some-id'))
// Will commit an event with the following payload:
// { id: 'some-id', value: { someField: 'explicit-value', someOtherField: 'some-other-default-value' } }

Similar to .get, you can omit the id argument if you've set a default id.

@example

const uiState = State.SQLite.clientDocument({
name: 'UiState',
schema: Schema.Struct({ someField: Schema.String }),
default: { id: SessionIdSymbol, value: { someField: 'some-default-value' } },
})
const setEventDef = store.commit(uiState.set({ someField: 'explicit-value' }))
// Will commit an event with the following payload:
// { id: '...', value: { someField: 'explicit-value' } }
// ^^^
// Automatically replaced with the client session id

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
})

Define SQLite tables using explicit column definitions:

import {
import State
State
} from '@livestore/livestore'
export const
const userTable: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"users", {
readonly id: {
columnType: "text";
schema: Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly email: {
columnType: "text";
schema: Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly age: {
columnType: "integer";
... 4 more ...;
autoIncrement: false;
};
readonly isActive: {
...;
};
readonly metadata: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema<...>>
userTable
=
import State
State
.
import SQLite
SQLite
.
function table<"users", {
readonly id: {
columnType: "text";
schema: Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly email: {
columnType: "text";
schema: Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly age: {
columnType: "integer";
... 4 more ...;
autoIncrement: false;
};
readonly isActive: {
...;
};
readonly metadata: {
...;
};
}, {
...;
}>(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:

  1. Using explicit column definitions
  2. Using an Effect Schema (either the name property needs to be provided or the schema needs to have a title/identifier)
// Using explicit columns
const 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 annotations
import { 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 name
const 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 indexes
const 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: "users"
name
: 'users',
columns: {
readonly id: {
columnType: "text";
schema: Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly email: {
columnType: "text";
schema: Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly age: {
columnType: "integer";
... 4 more ...;
autoIncrement: false;
};
readonly isActive: {
...;
};
readonly metadata: {
...;
};
} & {
...;
}
columns
: {
id: {
columnType: "text";
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<string, string, never>;
default?: typeof NoDefault;
nullable?: false;
primaryKey?: true;
autoIncrement?: false;
}) => {
columnType: "text";
schema: Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
} (+1 overload)
text
({
primaryKey?: true
primaryKey
: true }),
email: {
columnType: "text";
schema: Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
email
:
import State
State
.
import SQLite
SQLite
.
const text: () => {
columnType: "text";
schema: Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
text
(),
name: {
columnType: "text";
schema: Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
name
:
import State
State
.
import SQLite
SQLite
.
const text: () => {
columnType: "text";
schema: Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
text
(),
age: {
columnType: "integer";
schema: Schema<number, number, never>;
default: Some<0>;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
age
:
import State
State
.
import SQLite
SQLite
.
const integer: <number, number, false, 0, false, false>(args: {
schema?: Schema<number, number, never>;
default?: 0;
nullable?: false;
primaryKey?: false;
autoIncrement?: false;
}) => {
columnType: "integer";
schema: Schema<number, number, never>;
default: Some<0>;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
integer
({
default?: 0
default
: 0 }),
isActive: {
columnType: "integer";
schema: Schema<boolean, number, never>;
default: Some<true>;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
isActive
:
import State
State
.
import SQLite
SQLite
.
const boolean: <boolean, false, true, false, false>(args: {
default?: true;
nullable?: false;
primaryKey?: false;
autoIncrement?: false;
}) => {
columnType: "integer";
schema: Schema<boolean, number, never>;
default: Some<true>;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
boolean
({
default?: true
default
: true }),
metadata: {
columnType: "text";
schema: Schema<unknown, string | null, never>;
default: Some<any> | None<never>;
nullable: true;
primaryKey: false;
autoIncrement: false;
}
metadata
:
import State
State
.
import SQLite
SQLite
.
const json: <unknown, true, any, false, false>(args: {
schema?: Schema<unknown, any, never>;
default?: any;
nullable?: true;
primaryKey?: false;
autoIncrement?: false;
}) => {
columnType: "text";
schema: Schema<unknown, string | null, never>;
default: Some<any> | None<never>;
nullable: true;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
json
({
nullable?: true
nullable
: true }),
},
indexes?: [{
readonly name: "idx_users_email";
readonly columns: readonly ["email"];
readonly isUnique: true;
}]
indexes
: [{
name: "idx_users_email"
name
: 'idx_users_email',
columns: readonly ["email"]
columns
: ['email'],
isUnique: true
isUnique
: true }],
})

Use the optional indexes array to declare secondary indexes or enforce uniqueness (set isUnique: true).

You can use these column types when defining tables:

  • State.SQLite.text: A text field, returns string.
  • State.SQLite.integer: An integer field, returns number.
  • State.SQLite.real: A real field (floating point number), returns number.
  • State.SQLite.blob: A blob field (binary data), returns Uint8Array.
  • State.SQLite.boolean: An integer field that stores 0 for false and 1 for true and returns a boolean.
  • State.SQLite.json: A text field that stores a stringified JSON object and returns a decoded JSON value.
  • State.SQLite.datetime: A text field that stores dates as ISO 8601 strings and returns a Date.
  • State.SQLite.datetimeInteger: A integer field that stores dates as the number of milliseconds since the epoch and returns a Date.

You can also provide a custom schema for a column which is used to automatically encode and decode the column value.

import {
import Schema
Schema
,
import State
State
} from '@livestore/livestore'
export const
const UserMetadata: Schema.Struct<{
petName: typeof Schema.String;
favoriteColor: Schema.Literal<["red", "blue", "green"]>;
}>
UserMetadata
=
import Schema
Schema
.
function Struct<{
petName: typeof Schema.String;
favoriteColor: Schema.Literal<["red", "blue", "green"]>;
}>(fields: {
petName: typeof Schema.String;
favoriteColor: Schema.Literal<["red", "blue", "green"]>;
}): Schema.Struct<{
petName: typeof Schema.String;
favoriteColor: Schema.Literal<["red", "blue", "green"]>;
}> (+1 overload)

@since3.10.0

Struct
({
petName: typeof Schema.String
petName
:
import Schema
Schema
.
class String
export String

@since3.10.0

String
,
favoriteColor: Schema.Literal<["red", "blue", "green"]>
favoriteColor
:
import Schema
Schema
.
function Literal<["red", "blue", "green"]>(literals_0: "red", literals_1: "blue", literals_2: "green"): Schema.Literal<["red", "blue", "green"]> (+2 overloads)

@since3.10.0

Literal
('red', 'blue', 'green'),
})
export const
const userTable: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"user", {
readonly id: {
columnType: "text";
schema: Schema.Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Schema.Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly metadata: {
columnType: "text";
schema: Schema.Schema<{
readonly petName: string;
readonly favoriteColor: "red" | "blue" | "green";
}, string, never>;
default: Some<any> | None<...>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>
userTable
=
import State
State
.
import SQLite
SQLite
.
function table<"user", {
readonly id: {
columnType: "text";
schema: Schema.Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Schema.Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly metadata: {
columnType: "text";
schema: Schema.Schema<{
readonly petName: string;
readonly favoriteColor: "red" | "blue" | "green";
}, string, never>;
default: Some<any> | None<...>;
nullable: false;
primaryKey: false;
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:

  1. Using explicit column definitions
  2. Using an Effect Schema (either the name property needs to be provided or the schema needs to have a title/identifier)
// Using explicit columns
const 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 annotations
import { 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 name
const 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 indexes
const 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: "user"
name
: 'user',
columns: {
readonly id: {
columnType: "text";
schema: Schema.Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Schema.Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly metadata: {
columnType: "text";
schema: Schema.Schema<{
readonly petName: string;
readonly favoriteColor: "red" | "blue" | "green";
}, string, never>;
default: Some<any> | None<...>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}
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 }),
name: {
columnType: "text";
schema: Schema.Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
name
:
import State
State
.
import SQLite
SQLite
.
const text: () => {
columnType: "text";
schema: Schema.Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
text
(),
metadata: {
columnType: "text";
schema: Schema.Schema<{
readonly petName: string;
readonly favoriteColor: "red" | "blue" | "green";
}, string, never>;
default: Some<any> | None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
metadata
:
import State
State
.
import SQLite
SQLite
.
const json: <{
readonly petName: string;
readonly favoriteColor: "red" | "blue" | "green";
}, false, any, false, false>(args: {
schema?: Schema.Schema<{
readonly petName: string;
readonly favoriteColor: "red" | "blue" | "green";
}, any, never>;
default?: any;
nullable?: false;
primaryKey?: false;
autoIncrement?: false;
}) => {
columnType: "text";
schema: Schema.Schema<{
readonly petName: string;
readonly favoriteColor: "red" | "blue" | "green";
}, string, never>;
default: Some<any> | None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
json
({
schema?: Schema.Schema<{
readonly petName: string;
readonly favoriteColor: "red" | "blue" | "green";
}, any, never>
schema
:
const UserMetadata: Schema.Struct<{
petName: typeof Schema.String;
favoriteColor: Schema.Literal<["red", "blue", "green"]>;
}>
UserMetadata
}),
},
})

Migration strategies:

  • auto: Automatically migrate the database to the newest schema and rematerializes the state from the eventlog.
  • manual: Manually migrate the database to the newest schema.
  • Meant for convenience
  • Client-only
  • Goal: Similar ease of use as React.useState
  • When schema changes in a non-backwards compatible way, previous events are dropped and the state is reset
    • Don’t use client documents for sensitive data which must not be lost
  • Implies
    • Table with id and value columns
    • ${MyTable}Set event + materializer (which are auto-registered)
import {
import Schema
Schema
,
type SessionIdSymbol = typeof SessionIdSymbol
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
,
import State
State
, type
class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>
Store
} from '@livestore/livestore'
import {
const useClientDocument: {
<TTableDef extends State.SQLite.ClientDocumentTableDef.Trait<any, any, any, {
partialSet: boolean;
default: any;
}>>(table: TTableDef, id?: State.SQLite.ClientDocumentTableDef.DefaultIdType<TTableDef> | SessionIdSymbol, options?: Partial<RowQuery.GetOrCreateOptions<TTableDef>>): UseClientDocumentResult<TTableDef>;
<TTableDef extends State.SQLite.ClientDocumentTableDef.Trait<any, any, any, {
partialSet: boolean;
default: any;
}>>(table: TTableDef, id: State.SQLite.ClientDocumentTableDef.DefaultIdType<TTableDef> | string | SessionIdSymbol, options?: Partial<RowQuery.GetOrCreateOptions<TTableDef>>): UseClientDocumentResult<TTableDef>;
}

Similar to React.useState but returns a tuple of [state, setState, id, query$] for a given table where ...

  • state is the current value of the row (fully decoded according to the table schema)
  • setState is a function that can be used to update the document
  • id is the id of the document
  • query$ is a LiveQuery that e.g. can be used to subscribe to changes to the document

useClientDocument only works for client-document tables:

const MyState = State.SQLite.clientDocument({
name: 'MyState',
schema: Schema.Struct({
showSidebar: Schema.Boolean,
}),
default: { id: SessionIdSymbol, value: { showSidebar: true } },
})
const MyComponent = () => {
const [{ showSidebar }, setState] = useClientDocument(MyState)
return (
<div onClick={() => setState({ showSidebar: !showSidebar })}>
{showSidebar ? 'Sidebar is open' : 'Sidebar is closed'}
</div>
)
}

If the table has a default id, useClientDocument can be called without an id argument. Otherwise, the id argument is required.

useClientDocument
} from '@livestore/react'
import
(alias) namespace React
import React
React
from 'react'
export const
const uiState: State.SQLite.ClientDocumentTableDef<"UiState", {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
readonly filter: "all" | "active" | "completed";
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: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
readonly filter: "all" | "active" | "completed";
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: "all" | "active" | "completed";
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 SessionIdSymbol
store.queryDb(clientDocumentTable.get('some-id'))
// Setting data
// Again, `'some-id'` can be ommited for SessionIdSymbol
store.commit(clientDocumentTable.set({ someField: 'some-value' }, 'some-id'))

clientDocument
({
name: "UiState"
name
: 'UiState',
schema: Schema.Schema<{
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
readonly filter: "all" | "active" | "completed";
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)

@since3.10.0

Struct
({
newTodoText: typeof Schema.String
newTodoText
:
import Schema
Schema
.
class String
export String

@since3.10.0

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)

@since3.10.0

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' } },
})
export const
const readUiState: (store: Store) => {
newTodoText: string;
filter: "all" | "active" | "completed";
}
readUiState
= (
store: Store<LiveStoreSchema.Any, {}>
store
:
class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>
Store
): {
newTodoText: string
newTodoText
: string;
filter: "all" | "active" | "completed"
filter
: 'all' | 'active' | 'completed' } =>
store: Store<LiveStoreSchema.Any, {}>
store
.
Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.query: <{
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}>(query: Queryable<{
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}> | {
query: string;
bindValues: Bindable;
schema?: Schema.Schema<{
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, never>;
}, options?: {
otelContext?: Context;
debugRefreshReason?: RefreshReason;
}) => {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}

Synchronously queries the database without creating a LiveQuery. This is useful for queries that don't need to be reactive.

Example: Query builder

const completedTodos = store.query(tables.todo.where({ complete: true }))

Example: Raw SQL query

const completedTodos = store.query({ query: 'SELECT * FROM todo WHERE complete = 1', bindValues: {} })

query
(
const uiState: State.SQLite.ClientDocumentTableDef<"UiState", {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
readonly filter: "all" | "active" | "completed";
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: "all" | "active" | "completed"; readonly newTodoText: string; }, { readonly filter: "all" | "active" | "completed"; readonly newTodoText: string; }, { ...; }>.get: (id?: typeof SessionIdSymbol | undefined, options?: {
default: Partial<{
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}>;
} | undefined) => QueryBuilder<{
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, State.SQLite.ClientDocumentTableDef.TableDefBase_<"UiState", {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}>, QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase, TWithout extends QueryBuilder.ApiFeature = never>.ApiFeature>

Get the current value of the client document table.

@example

const someDocumentTable = State.SQLite.clientDocument({
name: 'SomeDocumentTable',
schema: Schema.Struct({
someField: Schema.String,
}),
default: { value: { someField: 'some-value' } },
})
const value$ = queryDb(someDocumentTable.get('some-id'))
// When you've set a default id, you can omit the id argument
const uiState = State.SQLite.clientDocument({
name: 'UiState',
schema: Schema.Struct({
someField: Schema.String,
}),
default: { id: SessionIdSymbol, value: { someField: 'some-value' } },
})
const value$ = queryDb(uiState.get())

get
())
export const
const setNewTodoText: (store: Store, newTodoText: string) => void
setNewTodoText
= (
store: Store<LiveStoreSchema.Any, {}>
store
:
class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>
Store
,
newTodoText: string
newTodoText
: string): void => {
store: Store<LiveStoreSchema.Any, {}>
store
.
Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.commit: <readonly [{
name: "UiStateSet";
args: {
id: string;
value: {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
};
};
}]>(list_0: {
name: "UiStateSet";
args: {
id: string;
value: {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
};
};
}) => void (+3 overloads)
commit
(
const uiState: State.SQLite.ClientDocumentTableDef<"UiState", {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
readonly filter: "all" | "active" | "completed";
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: "all" | "active" | "completed"; readonly newTodoText: string; }, { readonly filter: "all" | "active" | "completed"; readonly newTodoText: string; }, { ...; }>.set: (args: Partial<{
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}>, id?: string | SessionIdSymbol) => {
name: "UiStateSet";
args: {
id: string;
value: {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
};
};
}

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.

@example

const someDocumentTable = State.SQLite.clientDocument({
name: 'SomeDocumentTable',
schema: Schema.Struct({
someField: Schema.String,
someOtherField: Schema.String,
}),
default: { value: { someField: 'some-default-value', someOtherField: 'some-other-default-value' } },
})
const setEventDef = store.commit(someDocumentTable.set({ someField: 'explicit-value' }, 'some-id'))
// Will commit an event with the following payload:
// { id: 'some-id', value: { someField: 'explicit-value', someOtherField: 'some-other-default-value' } }

Similar to .get, you can omit the id argument if you've set a default id.

@example

const uiState = State.SQLite.clientDocument({
name: 'UiState',
schema: Schema.Struct({ someField: Schema.String }),
default: { id: SessionIdSymbol, value: { someField: 'some-default-value' } },
})
const setEventDef = store.commit(uiState.set({ someField: 'explicit-value' }))
// Will commit an event with the following payload:
// { id: '...', value: { someField: 'explicit-value' } }
// ^^^
// Automatically replaced with the client session id

set
({
newTodoText?: string
newTodoText
}))
}
export const
const UiStateFilter: React.FC<{}>
UiStateFilter
:
(alias) namespace React
import 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.

@seehttps://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components React TypeScript Cheatsheet

@aliasfor FunctionComponent

@example

// With props:
type Props = { name: string }
const MyComponent: FC<Props> = (props) => {
return <div>{props.name}</div>
}

@example

// Without props:
const MyComponentWithoutProps: FC = () => {
return <div>MyComponentWithoutProps</div>
}

FC
= () => {
const [
const state: {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}
state
,
const setState: StateSetters<State.SQLite.ClientDocumentTableDef<"UiState", {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
partialSet: true;
default: {
id: typeof SessionIdSymbol;
value: {
readonly newTodoText: "";
readonly filter: "all";
};
};
}>>
setState
] =
useClientDocument<State.SQLite.ClientDocumentTableDef<"UiState", {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
partialSet: true;
default: {
id: typeof SessionIdSymbol;
value: {
readonly newTodoText: "";
readonly filter: "all";
};
};
}>>(table: State.SQLite.ClientDocumentTableDef<"UiState", {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
partialSet: true;
default: {
id: typeof SessionIdSymbol;
value: {
readonly newTodoText: "";
readonly filter: "all";
};
};
}>, id?: typeof SessionIdSymbol | undefined, options?: Partial<...> | undefined): UseClientDocumentResult<...> (+1 overload)

Similar to React.useState but returns a tuple of [state, setState, id, query$] for a given table where ...

  • state is the current value of the row (fully decoded according to the table schema)
  • setState is a function that can be used to update the document
  • id is the id of the document
  • query$ is a LiveQuery that e.g. can be used to subscribe to changes to the document

useClientDocument only works for client-document tables:

const MyState = State.SQLite.clientDocument({
name: 'MyState',
schema: Schema.Struct({
showSidebar: Schema.Boolean,
}),
default: { id: SessionIdSymbol, value: { showSidebar: true } },
})
const MyComponent = () => {
const [{ showSidebar }, setState] = useClientDocument(MyState)
return (
<div onClick={() => setState({ showSidebar: !showSidebar })}>
{showSidebar ? 'Sidebar is open' : 'Sidebar is closed'}
</div>
)
}

If the table has a default id, useClientDocument can be called without an id argument. Otherwise, the id argument is required.

useClientDocument
(
const uiState: State.SQLite.ClientDocumentTableDef<"UiState", {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
partialSet: true;
default: {
id: typeof SessionIdSymbol;
value: {
readonly newTodoText: "";
readonly filter: "all";
};
};
}>
uiState
)
const
const showActive: () => void
showActive
=
(alias) namespace React
import React
React
.
function React.useCallback<() => void>(callback: () => void, deps: React.DependencyList): () => void

useCallback will return a memoized version of the callback that only changes if one of the inputs has changed.

useCallback
(() => {
const setState: (action: SetStateActionPartial<{
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}>) => void
setState
({
filter?: "all" | "active" | "completed"
filter
: 'active' })
}, [
const setState: StateSetters<State.SQLite.ClientDocumentTableDef<"UiState", {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
partialSet: true;
default: {
id: typeof SessionIdSymbol;
value: {
readonly newTodoText: "";
readonly filter: "all";
};
};
}>>
setState
])
const
const showAll: () => void
showAll
=
(alias) namespace React
import React
React
.
function React.useCallback<() => void>(callback: () => void, deps: React.DependencyList): () => void

useCallback will return a memoized version of the callback that only changes if one of the inputs has changed.

useCallback
(() => {
const setState: (action: SetStateActionPartial<{
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}>) => void
setState
({
filter?: "all" | "active" | "completed"
filter
: 'all' })
}, [
const setState: StateSetters<State.SQLite.ClientDocumentTableDef<"UiState", {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}, {
partialSet: true;
default: {
id: typeof SessionIdSymbol;
value: {
readonly newTodoText: "";
readonly filter: "all";
};
};
}>>
setState
])
return (
<
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
<
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
React.ButtonHTMLAttributes<HTMLButtonElement>.type?: "button" | "submit" | "reset" | undefined
type
="button"
React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
onClick
={
const showAll: () => void
showAll
}>
All
</
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>
<
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
React.ButtonHTMLAttributes<HTMLButtonElement>.type?: "button" | "submit" | "reset" | undefined
type
="button"
React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
onClick
={
const showActive: () => void
showActive
}>
Active ({
const state: {
readonly filter: "all" | "active" | "completed";
readonly newTodoText: string;
}
state
.
filter: "all" | "active" | "completed"
filter
=== 'active' ? 'selected' : 'select'})
</
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>
</
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
)
}

Sometimes you want a simple key-value store for arbitrary values without partial merging. You can model this by using Schema.Any as the value schema. With Schema.Any, updates fully replace the stored value (no partial merge semantics).

import {
import Schema
Schema
,
import State
State
, type
class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>
Store
} from '@livestore/livestore'
import {
const useClientDocument: {
<TTableDef extends State.SQLite.ClientDocumentTableDef.Trait<any, any, any, {
partialSet: boolean;
default: any;
}>>(table: TTableDef, id?: State.SQLite.ClientDocumentTableDef.DefaultIdType<TTableDef> | SessionIdSymbol, options?: Partial<RowQuery.GetOrCreateOptions<TTableDef>>): UseClientDocumentResult<TTableDef>;
<TTableDef extends State.SQLite.ClientDocumentTableDef.Trait<any, any, any, {
partialSet: boolean;
default: any;
}>>(table: TTableDef, id: State.SQLite.ClientDocumentTableDef.DefaultIdType<TTableDef> | string | SessionIdSymbol, options?: Partial<RowQuery.GetOrCreateOptions<TTableDef>>): UseClientDocumentResult<TTableDef>;
}

Similar to React.useState but returns a tuple of [state, setState, id, query$] for a given table where ...

  • state is the current value of the row (fully decoded according to the table schema)
  • setState is a function that can be used to update the document
  • id is the id of the document
  • query$ is a LiveQuery that e.g. can be used to subscribe to changes to the document

useClientDocument only works for client-document tables:

const MyState = State.SQLite.clientDocument({
name: 'MyState',
schema: Schema.Struct({
showSidebar: Schema.Boolean,
}),
default: { id: SessionIdSymbol, value: { showSidebar: true } },
})
const MyComponent = () => {
const [{ showSidebar }, setState] = useClientDocument(MyState)
return (
<div onClick={() => setState({ showSidebar: !showSidebar })}>
{showSidebar ? 'Sidebar is open' : 'Sidebar is closed'}
</div>
)
}

If the table has a default id, useClientDocument can be called without an id argument. Otherwise, the id argument is required.

useClientDocument
} from '@livestore/react'
import type
(alias) namespace React
import React
React
from 'react'
export const
const kv: State.SQLite.ClientDocumentTableDef<"Kv", any, any, {
partialSet: false;
default: {
id: undefined;
value: null;
};
}>
kv
=
import State
State
.
import SQLite
SQLite
.
clientDocument<"Kv", any, any, {
readonly name: "Kv";
readonly schema: typeof Schema.Any;
readonly default: {
readonly value: null;
};
}>({ name, schema: valueSchema, ...inputOptions }: {
name: "Kv";
schema: Schema.Schema<any, any, never>;
} & {
readonly name: "Kv";
readonly schema: typeof Schema.Any;
readonly default: {
readonly value: null;
};
}): State.SQLite.ClientDocumentTableDef<"Kv", any, any, {
partialSet: false;
default: {
id: undefined;
value: null;
};
}>
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 SessionIdSymbol
store.queryDb(clientDocumentTable.get('some-id'))
// Setting data
// Again, `'some-id'` can be ommited for SessionIdSymbol
store.commit(clientDocumentTable.set({ someField: 'some-value' }, 'some-id'))

clientDocument
({
name: "Kv"
name
: 'Kv',
schema: Schema.Schema<any, any, never> & typeof Schema.Any
schema
:
import Schema
Schema
.
class Any

@since3.10.0

Any
,
default: {
readonly value: null;
}
default
: {
value: null
value
: null },
})
export const
const readKvValue: (store: Store, id: string) => unknown
readKvValue
= (
store: Store<LiveStoreSchema.Any, {}>
store
:
class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>
Store
,
id: string
id
: string): unknown =>
store: Store<LiveStoreSchema.Any, {}>
store
.
Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.query: <any>(query: Queryable<any> | {
query: string;
bindValues: Bindable;
schema?: Schema.Schema<any, any, never>;
}, options?: {
otelContext?: Context;
debugRefreshReason?: RefreshReason;
}) => any

Synchronously queries the database without creating a LiveQuery. This is useful for queries that don't need to be reactive.

Example: Query builder

const completedTodos = store.query(tables.todo.where({ complete: true }))

Example: Raw SQL query

const completedTodos = store.query({ query: 'SELECT * FROM todo WHERE complete = 1', bindValues: {} })

query
(
const kv: State.SQLite.ClientDocumentTableDef<"Kv", any, any, {
partialSet: false;
default: {
id: undefined;
value: null;
};
}>
kv
.
ClientDocumentTableDef<TName extends string, TType, TEncoded, TOptions extends ClientDocumentTableOptions<TType>>.Trait<"Kv", any, any, { partialSet: false; default: { id: undefined; value: null; }; }>.get: (id: string | SessionIdSymbol, options?: {
default: Partial<any>;
} | undefined) => QueryBuilder<any, State.SQLite.ClientDocumentTableDef.TableDefBase_<"Kv", any>, QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase, TWithout extends QueryBuilder.ApiFeature = never>.ApiFeature>

Get the current value of the client document table.

@example

const someDocumentTable = State.SQLite.clientDocument({
name: 'SomeDocumentTable',
schema: Schema.Struct({
someField: Schema.String,
}),
default: { value: { someField: 'some-value' } },
})
const value$ = queryDb(someDocumentTable.get('some-id'))
// When you've set a default id, you can omit the id argument
const uiState = State.SQLite.clientDocument({
name: 'UiState',
schema: Schema.Struct({
someField: Schema.String,
}),
default: { id: SessionIdSymbol, value: { someField: 'some-value' } },
})
const value$ = queryDb(uiState.get())

get
(
id: string
id
))
export const
const setKvValue: (store: Store, id: string, value: unknown) => void
setKvValue
= (
store: Store<LiveStoreSchema.Any, {}>
store
:
class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>
Store
,
id: string
id
: string,
value: unknown
value
: unknown): void => {
store: Store<LiveStoreSchema.Any, {}>
store
.
Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.commit: <readonly [{
name: "KvSet";
args: {
id: string;
value: any;
};
}]>(list_0: {
name: "KvSet";
args: {
id: string;
value: any;
};
}) => void (+3 overloads)
commit
(
const kv: State.SQLite.ClientDocumentTableDef<"Kv", any, any, {
partialSet: false;
default: {
id: undefined;
value: null;
};
}>
kv
.
ClientDocumentTableDef<TName extends string, TType, TEncoded, TOptions extends ClientDocumentTableOptions<TType>>.Trait<"Kv", any, any, { partialSet: false; default: { id: undefined; value: null; }; }>.set: (args: any, id: string | SessionIdSymbol) => {
name: "KvSet";
args: {
id: string;
value: any;
};
}

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.

@example

const someDocumentTable = State.SQLite.clientDocument({
name: 'SomeDocumentTable',
schema: Schema.Struct({
someField: Schema.String,
someOtherField: Schema.String,
}),
default: { value: { someField: 'some-default-value', someOtherField: 'some-other-default-value' } },
})
const setEventDef = store.commit(someDocumentTable.set({ someField: 'explicit-value' }, 'some-id'))
// Will commit an event with the following payload:
// { id: 'some-id', value: { someField: 'explicit-value', someOtherField: 'some-other-default-value' } }

Similar to .get, you can omit the id argument if you've set a default id.

@example

const uiState = State.SQLite.clientDocument({
name: 'UiState',
schema: Schema.Struct({ someField: Schema.String }),
default: { id: SessionIdSymbol, value: { someField: 'some-default-value' } },
})
const setEventDef = store.commit(uiState.set({ someField: 'explicit-value' }))
// Will commit an event with the following payload:
// { id: '...', value: { someField: 'explicit-value' } }
// ^^^
// Automatically replaced with the client session id

set
(
value: unknown
value
,
id: string
id
))
}
export const
const KvViewer: React.FC<{
id: string;
}>
KvViewer
:
(alias) namespace React
import 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.

@seehttps://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components React TypeScript Cheatsheet

@aliasfor FunctionComponent

@example

// With props:
type Props = { name: string }
const MyComponent: FC<Props> = (props) => {
return <div>{props.name}</div>
}

@example

// Without props:
const MyComponentWithoutProps: FC = () => {
return <div>MyComponentWithoutProps</div>
}

FC
<{
id: string
id
: string }> = ({
id: string
id
}) => {
const [
const value: any
value
,
const setValue: StateSetters<State.SQLite.ClientDocumentTableDef<"Kv", any, any, {
partialSet: false;
default: {
id: undefined;
value: null;
};
}>>
setValue
] =
useClientDocument<State.SQLite.ClientDocumentTableDef<"Kv", any, any, {
partialSet: false;
default: {
id: undefined;
value: null;
};
}>>(table: State.SQLite.ClientDocumentTableDef<"Kv", any, any, {
partialSet: false;
default: {
id: undefined;
value: null;
};
}>, id: string | typeof SessionIdSymbol, options?: Partial<RowQuery.GetOrCreateOptions<State.SQLite.ClientDocumentTableDef<"Kv", any, any, {
partialSet: false;
default: {
id: undefined;
value: null;
};
}>>> | undefined): UseClientDocumentResult<State.SQLite.ClientDocumentTableDef<"Kv", any, any, {
partialSet: false;
default: {
id: undefined;
value: null;
};
}>> (+1 overload)

Similar to React.useState but returns a tuple of [state, setState, id, query$] for a given table where ...

  • state is the current value of the row (fully decoded according to the table schema)
  • setState is a function that can be used to update the document
  • id is the id of the document
  • query$ is a LiveQuery that e.g. can be used to subscribe to changes to the document

useClientDocument only works for client-document tables:

const MyState = State.SQLite.clientDocument({
name: 'MyState',
schema: Schema.Struct({
showSidebar: Schema.Boolean,
}),
default: { id: SessionIdSymbol, value: { showSidebar: true } },
})
const MyComponent = () => {
const [{ showSidebar }, setState] = useClientDocument(MyState)
return (
<div onClick={() => setState({ showSidebar: !showSidebar })}>
{showSidebar ? 'Sidebar is open' : 'Sidebar is closed'}
</div>
)
}

If the table has a default id, useClientDocument can be called without an id argument. Otherwise, the id argument is required.

useClientDocument
(
const kv: State.SQLite.ClientDocumentTableDef<"Kv", any, any, {
partialSet: false;
default: {
id: undefined;
value: null;
};
}>
kv
,
id: string
id
)
return (
<
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
React.ButtonHTMLAttributes<HTMLButtonElement>.type?: "button" | "submit" | "reset" | undefined
type
="button"
React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
onClick
={() =>
const setValue: (action: any) => void
setValue
('hello')}>
Current value: {
var JSON: JSON

An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.

JSON
.
JSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)

Converts a JavaScript value to a JavaScript Object Notation (JSON) string.

@paramvalue A JavaScript value, usually an object or array, to be converted.

@paramreplacer A function that transforms the results.

@paramspace Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read.

@throws{TypeError} If a circular reference or a BigInt value is found.

stringify
(
const value: any
value
)}
</
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>
)
}

You can use these column types:

  • State.SQLite.text: A text field, returns string.
  • State.SQLite.integer: An integer field, returns number.
  • State.SQLite.real: A real field (floating point number), returns number.
  • State.SQLite.blob: A blob field (binary data), returns Uint8Array.
  • State.SQLite.boolean: An integer field that stores 0 for false and 1 for true and returns a boolean.
  • State.SQLite.json: A text field that stores a stringified JSON object and returns a decoded JSON value.
  • State.SQLite.datetime: A text field that stores dates as ISO 8601 strings and returns a Date.
  • State.SQLite.datetimeInteger: A integer field that stores dates as the number of milliseconds since the epoch and returns a Date.

You can also provide a custom schema for a column which is used to automatically encode and decode the column value.

import {
import Schema
Schema
,
import State
State
} from '@livestore/livestore'
export const
const UserMetadata: Schema.Struct<{
petName: typeof Schema.String;
favoriteColor: Schema.Literal<["red", "blue", "green"]>;
}>
UserMetadata
=
import Schema
Schema
.
function Struct<{
petName: typeof Schema.String;
favoriteColor: Schema.Literal<["red", "blue", "green"]>;
}>(fields: {
petName: typeof Schema.String;
favoriteColor: Schema.Literal<["red", "blue", "green"]>;
}): Schema.Struct<{
petName: typeof Schema.String;
favoriteColor: Schema.Literal<["red", "blue", "green"]>;
}> (+1 overload)

@since3.10.0

Struct
({
petName: typeof Schema.String
petName
:
import Schema
Schema
.
class String
export String

@since3.10.0

String
,
favoriteColor: Schema.Literal<["red", "blue", "green"]>
favoriteColor
:
import Schema
Schema
.
function Literal<["red", "blue", "green"]>(literals_0: "red", literals_1: "blue", literals_2: "green"): Schema.Literal<["red", "blue", "green"]> (+2 overloads)

@since3.10.0

Literal
('red', 'blue', 'green'),
})
export const
const userTable: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"user", {
readonly id: {
columnType: "text";
schema: Schema.Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Schema.Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly metadata: {
columnType: "text";
schema: Schema.Schema<{
readonly petName: string;
readonly favoriteColor: "red" | "blue" | "green";
}, string, never>;
default: Some<any> | None<...>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>
userTable
=
import State
State
.
import SQLite
SQLite
.
function table<"user", {
readonly id: {
columnType: "text";
schema: Schema.Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Schema.Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly metadata: {
columnType: "text";
schema: Schema.Schema<{
readonly petName: string;
readonly favoriteColor: "red" | "blue" | "green";
}, string, never>;
default: Some<any> | None<...>;
nullable: false;
primaryKey: false;
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:

  1. Using explicit column definitions
  2. Using an Effect Schema (either the name property needs to be provided or the schema needs to have a title/identifier)
// Using explicit columns
const 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 annotations
import { 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 name
const 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 indexes
const 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: "user"
name
: 'user',
columns: {
readonly id: {
columnType: "text";
schema: Schema.Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Schema.Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly metadata: {
columnType: "text";
schema: Schema.Schema<{
readonly petName: string;
readonly favoriteColor: "red" | "blue" | "green";
}, string, never>;
default: Some<any> | None<...>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}
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 }),
name: {
columnType: "text";
schema: Schema.Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
name
:
import State
State
.
import SQLite
SQLite
.
const text: () => {
columnType: "text";
schema: Schema.Schema<string, string, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
text
(),
metadata: {
columnType: "text";
schema: Schema.Schema<{
readonly petName: string;
readonly favoriteColor: "red" | "blue" | "green";
}, string, never>;
default: Some<any> | None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
metadata
:
import State
State
.
import SQLite
SQLite
.
const json: <{
readonly petName: string;
readonly favoriteColor: "red" | "blue" | "green";
}, false, any, false, false>(args: {
schema?: Schema.Schema<{
readonly petName: string;
readonly favoriteColor: "red" | "blue" | "green";
}, any, never>;
default?: any;
nullable?: false;
primaryKey?: false;
autoIncrement?: false;
}) => {
columnType: "text";
schema: Schema.Schema<{
readonly petName: string;
readonly favoriteColor: "red" | "blue" | "green";
}, string, never>;
default: Some<any> | None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
json
({
schema?: Schema.Schema<{
readonly petName: string;
readonly favoriteColor: "red" | "blue" | "green";
}, any, never>
schema
:
const UserMetadata: Schema.Struct<{
petName: typeof Schema.String;
favoriteColor: Schema.Literal<["red", "blue", "green"]>;
}>
UserMetadata
}),
},
})
  • Use appropriate SQLite column types for your data (text, integer, real, blob)
  • Set primaryKey: true for primary key columns
  • Use nullable: true for columns that can contain NULL values
  • Provide meaningful default values where appropriate
  • Add unique constraints via table indexes using isUnique: true
  • Choose column types that match your data requirements
  • Use custom schemas with State.SQLite.json() for complex data structures
  • Group related table definitions in the same module
  • Use descriptive table and column names
  • It’s usually recommend to not distinguish between app state vs app data but rather keep all state in LiveStore.
    • This means you’ll rarely use React.useState when using LiveStore
  • In some cases for “fast changing values” it can make sense to keep a version of a state value outside of LiveStore with a reactive setter for React and a debounced setter for LiveStore to avoid excessive LiveStore mutations. Cases where this can make sense can include:
    • Text input / rich text editing
    • Scroll position tracking, resize events, move/drag events