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.Literals(['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.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>;
uiState: State.SQLite.ClientDocumentTableDef<...>;
}
tables
= {
todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
todos
:
import State
State
.
import SQLite
SQLite
.
function table<"todos", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}, 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.annotate({ 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,
}).annotate({ 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.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}
columns
: {
id: {
columnType: "text";
schema: Schema.Codec<string, string, never, 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.Codec<string, string, never, never>;
default?: typeof NoDefault;
nullable?: false;
primaryKey?: true;
autoIncrement?: false;
}) => {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
} (+1 overload)
text
({
primaryKey?: true
primaryKey
: true }),
text: {
columnType: "text";
schema: Schema.Codec<string, string, never, 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.Codec<string, string, never, never>;
default?: "";
nullable?: false;
primaryKey?: false;
autoIncrement?: false;
}) => {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
text
({
default?: ""
default
: '' }),
completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, 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.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
boolean
({
default?: false
default
: false }),
deletedAt: {
columnType: "integer";
schema: Schema.Codec<Date | null, number | null, never, 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.Codec<Date, number, never, never>;
default?: typeof NoDefault;
nullable?: true;
primaryKey?: false;
autoIncrement?: false;
}) => {
columnType: "integer";
schema: Schema.Codec<Date | null, number | null, never, never>;
default: None<never>;
nullable: true;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
integer
({
nullable?: true
nullable
: true,
schema?: Schema.Codec<Date, number, never, never>
schema
:
import Schema
Schema
.
const DateFromMillis: Schema.DateFromMillis

Type-level representation of

DateFromMillis

.

Schema that decodes epoch milliseconds into a JavaScript Date.

When to use

Use to model numeric millisecond timestamps that decode to JavaScript Date objects and encode back to numbers.

Details

Decoding: A number of milliseconds since the Unix epoch is decoded as a Date.

Encoding: A Date is encoded as its millisecond timestamp.

Gotchas

This schema accepts any number, including NaN, Infinity, and -Infinity. Those values decode to invalid Date instances.

@since4.0.0

@seeDateFromString for decoding string-encoded dates

@seeDateTimeUtcFromMillis for decoding epoch milliseconds into UTC values

@since4.0.0

DateFromMillis
}),
},
}),
// Client documents can be used for local-only state (e.g. form inputs)
uiState: State.SQLite.ClientDocumentTableDef<"uiState", Schema.Struct.ReadonlySide<{
readonly newTodoText: Schema.String;
readonly filter: Schema.Literals<readonly ["all", "active", "completed"]>;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly newTodoText: Schema.String;
readonly filter: Schema.Literals<readonly ["all", "active", "completed"]>;
}, "Encoded">, {
partialSet: true;
default: {
id: typeof SessionIdSymbol;
value: {
readonly newTodoText: "";
readonly filter: "all";
};
};
}>
uiState
:
import State
State
.
import SQLite
SQLite
.
clientDocument<"uiState", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly newTodoText: Schema.String;
readonly filter: Schema.Literals<readonly ["all", "active", "completed"]>;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly newTodoText: Schema.String;
readonly filter: Schema.Literals<readonly ["all", "active", "completed"]>;
}, "Encoded">, {
readonly name: "uiState";
readonly schema: Schema.Struct<{
readonly newTodoText: Schema.String;
readonly filter: Schema.Literals<readonly ["all", "active", "completed"]>;
}>;
readonly default: {
...;
};
}>({ name, schema: valueSchema, ...inputOptions }: {
...;
} & {
readonly name: "uiState";
readonly schema: Schema.Struct<{
readonly newTodoText: Schema.String;
readonly filter: Schema.Literals<readonly ["all", "active", "completed"]>;
}>;
readonly default: {
...;
};
}): 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.Codec<Schema.Struct.ReadonlySide<{
readonly newTodoText: Schema.String;
readonly filter: Schema.Literals<readonly ["all", "active", "completed"]>;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly newTodoText: Schema.String;
readonly filter: Schema.Literals<readonly ["all", "active", "completed"]>;
}, "Encoded">, never, never> & Schema.Struct<{
readonly newTodoText: Schema.String;
readonly filter: Schema.Literals<readonly ["all", "active", "completed"]>;
}>
schema
:
import Schema
Schema
.
function Struct<{
readonly newTodoText: Schema.String;
readonly filter: Schema.Literals<readonly ["all", "active", "completed"]>;
}>(fields: {
readonly newTodoText: Schema.String;
readonly filter: Schema.Literals<readonly ["all", "active", "completed"]>;
}): Schema.Struct<{
readonly newTodoText: Schema.String;
readonly filter: Schema.Literals<readonly ["all", "active", "completed"]>;
}>

Defines a struct schema from a map of field schemas.

Details

Each field value is a schema. Use

optionalKey

or

optional

to mark fields as optional, and

mutableKey

to mark them as mutable.

The resulting schema's Type is a readonly object type with the fields' decoded types. The Encoded form mirrors the field schemas' encoded types.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
const alice = Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 })
console.log(alice)
// { name: 'Alice', age: 30 }

@since3.10.0

Struct
({
newTodoText: Schema.String
newTodoText
:
import Schema
Schema
.
const String: Schema.String

Type-level representation of

String

.

Schema for string values. Validates that the input is typeof "string".

@since4.0.0

@since4.0.0

String
,
filter: Schema.Literals<readonly ["all", "active", "completed"]>
filter
:
import Schema
Schema
.
function Literals<readonly ["all", "active", "completed"]>(literals: readonly ["all", "active", "completed"]): Schema.Literals<readonly ["all", "active", "completed"]>

Creates a union schema from an array of literal values.

Example (Defining status codes)

import { Schema } from "effect"
const schema = Schema.Literals(["active", "inactive", "pending"])
// accepts "active", "inactive", or "pending"

@seeLiteral for a schema that represents a single literal.

@since4.0.0

Literals
(['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.Literals(['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", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Encoded">>;
todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">>;
todoUncompleted: State.SQLite.EventDef<...>;
todoDeleted: State.SQLite.EventDef<...>;
todoClearedCompleted: State.SQLite.EventDef<...>;
uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<...>;
}
events
= {
todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Encoded">>
todoCreated
:
import Events
Events
.
synced<"v1.TodoCreated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Encoded">>(args: {
name: "v1.TodoCreated";
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Encoded">, never, never>;
} & Omit<...>): State.SQLite.EventDef<...>
export synced

Creates a synced event definition.

Synced events are sent to the sync backend and distributed to all connected clients. Use this for collaborative data that should be shared across users and devices.

Event names should be versioned (e.g., v1.TodoCreated) to support schema evolution over time.

@example

import { Events } from '@livestore/livestore'
import { Schema } from 'effect'
const todoCreated = Events.synced({
name: 'v1.TodoCreated',
schema: Schema.Struct({
id: Schema.String,
text: Schema.String,
completed: Schema.Boolean,
}),
})
// Commit the event
store.commit(todoCreated({ id: 'abc', text: 'Buy milk', completed: false }))

synced
({
name: "v1.TodoCreated"
name
: 'v1.TodoCreated',
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Encoded">, never, never>
schema
:
import Schema
Schema
.
function Struct<{
readonly id: Schema.String;
readonly text: Schema.String;
}>(fields: {
readonly id: Schema.String;
readonly text: Schema.String;
}): Schema.Struct<{
readonly id: Schema.String;
readonly text: Schema.String;
}>

Defines a struct schema from a map of field schemas.

Details

Each field value is a schema. Use

optionalKey

or

optional

to mark fields as optional, and

mutableKey

to mark them as mutable.

The resulting schema's Type is a readonly object type with the fields' decoded types. The Encoded form mirrors the field schemas' encoded types.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
const alice = Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 })
console.log(alice)
// { name: 'Alice', age: 30 }

@since3.10.0

Struct
({
id: Schema.String
id
:
import Schema
Schema
.
const String: Schema.String

Type-level representation of

String

.

Schema for string values. Validates that the input is typeof "string".

@since4.0.0

@since4.0.0

String
,
text: Schema.String
text
:
import Schema
Schema
.
const String: Schema.String

Type-level representation of

String

.

Schema for string values. Validates that the input is typeof "string".

@since4.0.0

@since4.0.0

String
}),
}),
todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">>
todoCompleted
:
import Events
Events
.
synced<"v1.TodoCompleted", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">>(args: {
name: "v1.TodoCompleted";
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">, never, never>;
} & Omit<State.SQLite.DefineEventOptions<Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, false>, "derived" | "clientOnly">): State.SQLite.EventDef<...>
export synced

Creates a synced event definition.

Synced events are sent to the sync backend and distributed to all connected clients. Use this for collaborative data that should be shared across users and devices.

Event names should be versioned (e.g., v1.TodoCreated) to support schema evolution over time.

@example

import { Events } from '@livestore/livestore'
import { Schema } from 'effect'
const todoCreated = Events.synced({
name: 'v1.TodoCreated',
schema: Schema.Struct({
id: Schema.String,
text: Schema.String,
completed: Schema.Boolean,
}),
})
// Commit the event
store.commit(todoCreated({ id: 'abc', text: 'Buy milk', completed: false }))

synced
({
name: "v1.TodoCompleted"
name
: 'v1.TodoCompleted',
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">, never, never>
schema
:
import Schema
Schema
.
function Struct<{
readonly id: Schema.String;
}>(fields: {
readonly id: Schema.String;
}): Schema.Struct<{
readonly id: Schema.String;
}>

Defines a struct schema from a map of field schemas.

Details

Each field value is a schema. Use

optionalKey

or

optional

to mark fields as optional, and

mutableKey

to mark them as mutable.

The resulting schema's Type is a readonly object type with the fields' decoded types. The Encoded form mirrors the field schemas' encoded types.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
const alice = Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 })
console.log(alice)
// { name: 'Alice', age: 30 }

@since3.10.0

Struct
({
id: Schema.String
id
:
import Schema
Schema
.
const String: Schema.String

Type-level representation of

String

.

Schema for string values. Validates that the input is typeof "string".

@since4.0.0

@since4.0.0

String
}),
}),
todoUncompleted: State.SQLite.EventDef<"v1.TodoUncompleted", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">>
todoUncompleted
:
import Events
Events
.
synced<"v1.TodoUncompleted", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">>(args: {
name: "v1.TodoUncompleted";
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">, never, never>;
} & Omit<State.SQLite.DefineEventOptions<Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, false>, "derived" | "clientOnly">): State.SQLite.EventDef<...>
export synced

Creates a synced event definition.

Synced events are sent to the sync backend and distributed to all connected clients. Use this for collaborative data that should be shared across users and devices.

Event names should be versioned (e.g., v1.TodoCreated) to support schema evolution over time.

@example

import { Events } from '@livestore/livestore'
import { Schema } from 'effect'
const todoCreated = Events.synced({
name: 'v1.TodoCreated',
schema: Schema.Struct({
id: Schema.String,
text: Schema.String,
completed: Schema.Boolean,
}),
})
// Commit the event
store.commit(todoCreated({ id: 'abc', text: 'Buy milk', completed: false }))

synced
({
name: "v1.TodoUncompleted"
name
: 'v1.TodoUncompleted',
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">, never, never>
schema
:
import Schema
Schema
.
function Struct<{
readonly id: Schema.String;
}>(fields: {
readonly id: Schema.String;
}): Schema.Struct<{
readonly id: Schema.String;
}>

Defines a struct schema from a map of field schemas.

Details

Each field value is a schema. Use

optionalKey

or

optional

to mark fields as optional, and

mutableKey

to mark them as mutable.

The resulting schema's Type is a readonly object type with the fields' decoded types. The Encoded form mirrors the field schemas' encoded types.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
const alice = Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 })
console.log(alice)
// { name: 'Alice', age: 30 }

@since3.10.0

Struct
({
id: Schema.String
id
:
import Schema
Schema
.
const String: Schema.String

Type-level representation of

String

.

Schema for string values. Validates that the input is typeof "string".

@since4.0.0

@since4.0.0

String
}),
}),
todoDeleted: State.SQLite.EventDef<"v1.TodoDeleted", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly deletedAt: Schema.DateFromString;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly deletedAt: Schema.DateFromString;
}, "Encoded">>
todoDeleted
:
import Events
Events
.
synced<"v1.TodoDeleted", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.String;
readonly deletedAt: Schema.DateFromString;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly deletedAt: Schema.DateFromString;
}, "Encoded">>(args: {
name: "v1.TodoDeleted";
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly deletedAt: Schema.DateFromString;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly deletedAt: Schema.DateFromString;
}, "Encoded">, never, never>;
} & Omit<...>): State.SQLite.EventDef<...>
export synced

Creates a synced event definition.

Synced events are sent to the sync backend and distributed to all connected clients. Use this for collaborative data that should be shared across users and devices.

Event names should be versioned (e.g., v1.TodoCreated) to support schema evolution over time.

@example

import { Events } from '@livestore/livestore'
import { Schema } from 'effect'
const todoCreated = Events.synced({
name: 'v1.TodoCreated',
schema: Schema.Struct({
id: Schema.String,
text: Schema.String,
completed: Schema.Boolean,
}),
})
// Commit the event
store.commit(todoCreated({ id: 'abc', text: 'Buy milk', completed: false }))

synced
({
name: "v1.TodoDeleted"
name
: 'v1.TodoDeleted',
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly deletedAt: Schema.DateFromString;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly deletedAt: Schema.DateFromString;
}, "Encoded">, never, never>
schema
:
import Schema
Schema
.
function Struct<{
readonly id: Schema.String;
readonly deletedAt: Schema.DateFromString;
}>(fields: {
readonly id: Schema.String;
readonly deletedAt: Schema.DateFromString;
}): Schema.Struct<{
readonly id: Schema.String;
readonly deletedAt: Schema.DateFromString;
}>

Defines a struct schema from a map of field schemas.

Details

Each field value is a schema. Use

optionalKey

or

optional

to mark fields as optional, and

mutableKey

to mark them as mutable.

The resulting schema's Type is a readonly object type with the fields' decoded types. The Encoded form mirrors the field schemas' encoded types.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
const alice = Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 })
console.log(alice)
// { name: 'Alice', age: 30 }

@since3.10.0

Struct
({
id: Schema.String
id
:
import Schema
Schema
.
const String: Schema.String

Type-level representation of

String

.

Schema for string values. Validates that the input is typeof "string".

@since4.0.0

@since4.0.0

String
,
deletedAt: Schema.DateFromString
deletedAt
:
import Schema
Schema
.
const DateFromString: Schema.DateFromString

Type-level representation of

DateFromString

.

Schema that decodes a string into a JavaScript Date.

When to use

Use to model string-encoded dates that decode to JavaScript Date objects and encode back to strings.

Details

Decoding: The string is passed to JavaScript Date construction.

Encoding: A valid Date is encoded as an ISO string; an invalid Date is encoded as "Invalid Date".

Gotchas

Invalid date strings can decode to invalid Date instances.

@since3.10.0

@seeDateFromMillis for decoding epoch milliseconds into Date instances

@seeDateTimeUtcFromString for decoding date-time strings into UTC values

@seeDate for accepting Date instances directly

@seeDateValid for rejecting invalid Date instances

@since3.10.0

DateFromString
.
Bottom<unknown, unknown, unknown, unknown, Declaration, decodeTo<Date, String, never, never>, unknown, unknown, readonly [], unknown, "readonly", "required", "no-default", "readonly", "required">.check(checks_0: Check<Date>, ...checks: Check<Date>[]): Schema.DateFromString
check
(
import Schema
Schema
.
function isDateValid(annotations?: Schema.Annotations.Filter): Filter<globalThis.Date>

Validates that a Date object represents a valid date (not an invalid date like new Date("invalid")).

Details

JSON Schema:

This check does not have a direct JSON Schema equivalent, as JSON Schema validates date strings, not Date objects.

Arbitrary:

When generating test data with fast-check, this applies a valid: true constraint to ensure generated Date objects are valid.

@since4.0.0

isDateValid
()),
}),
}),
todoClearedCompleted: State.SQLite.EventDef<"v1.TodoClearedCompleted", Schema.Struct.ReadonlySide<{
readonly deletedAt: Schema.DateFromString;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly deletedAt: Schema.DateFromString;
}, "Encoded">>
todoClearedCompleted
:
import Events
Events
.
synced<"v1.TodoClearedCompleted", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly deletedAt: Schema.DateFromString;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly deletedAt: Schema.DateFromString;
}, "Encoded">>(args: {
name: "v1.TodoClearedCompleted";
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly deletedAt: Schema.DateFromString;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly deletedAt: Schema.DateFromString;
}, "Encoded">, never, never>;
} & Omit<...>): State.SQLite.EventDef<...>
export synced

Creates a synced event definition.

Synced events are sent to the sync backend and distributed to all connected clients. Use this for collaborative data that should be shared across users and devices.

Event names should be versioned (e.g., v1.TodoCreated) to support schema evolution over time.

@example

import { Events } from '@livestore/livestore'
import { Schema } from 'effect'
const todoCreated = Events.synced({
name: 'v1.TodoCreated',
schema: Schema.Struct({
id: Schema.String,
text: Schema.String,
completed: Schema.Boolean,
}),
})
// Commit the event
store.commit(todoCreated({ id: 'abc', text: 'Buy milk', completed: false }))

synced
({
name: "v1.TodoClearedCompleted"
name
: 'v1.TodoClearedCompleted',
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly deletedAt: Schema.DateFromString;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly deletedAt: Schema.DateFromString;
}, "Encoded">, never, never>
schema
:
import Schema
Schema
.
function Struct<{
readonly deletedAt: Schema.DateFromString;
}>(fields: {
readonly deletedAt: Schema.DateFromString;
}): Schema.Struct<{
readonly deletedAt: Schema.DateFromString;
}>

Defines a struct schema from a map of field schemas.

Details

Each field value is a schema. Use

optionalKey

or

optional

to mark fields as optional, and

mutableKey

to mark them as mutable.

The resulting schema's Type is a readonly object type with the fields' decoded types. The Encoded form mirrors the field schemas' encoded types.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
const alice = Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 })
console.log(alice)
// { name: 'Alice', age: 30 }

@since3.10.0

Struct
({
deletedAt: Schema.DateFromString
deletedAt
:
import Schema
Schema
.
const DateFromString: Schema.DateFromString

Type-level representation of

DateFromString

.

Schema that decodes a string into a JavaScript Date.

When to use

Use to model string-encoded dates that decode to JavaScript Date objects and encode back to strings.

Details

Decoding: The string is passed to JavaScript Date construction.

Encoding: A valid Date is encoded as an ISO string; an invalid Date is encoded as "Invalid Date".

Gotchas

Invalid date strings can decode to invalid Date instances.

@since3.10.0

@seeDateFromMillis for decoding epoch milliseconds into Date instances

@seeDateTimeUtcFromString for decoding date-time strings into UTC values

@seeDate for accepting Date instances directly

@seeDateValid for rejecting invalid Date instances

@since3.10.0

DateFromString
.
Bottom<unknown, unknown, unknown, unknown, Declaration, decodeTo<Date, String, never, never>, unknown, unknown, readonly [], unknown, "readonly", "required", "no-default", "readonly", "required">.check(checks_0: Check<Date>, ...checks: Check<Date>[]): Schema.DateFromString
check
(
import Schema
Schema
.
function isDateValid(annotations?: Schema.Annotations.Filter): Filter<globalThis.Date>

Validates that a Date object represents a valid date (not an invalid date like new Date("invalid")).

Details

JSON Schema:

This check does not have a direct JSON Schema equivalent, as JSON Schema validates date strings, not Date objects.

Arbitrary:

When generating test data with fast-check, this applies a valid: true constraint to ensure generated Date objects are valid.

@since4.0.0

isDateValid
()) }),
}),
uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<"uiState", Schema.Struct.ReadonlySide<{
readonly newTodoText: Schema.String;
readonly filter: Schema.Literals<readonly ["all", "active", "completed"]>;
}, "Type">, {
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.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>;
uiState: State.SQLite.ClientDocumentTableDef<...>;
}
tables
.
uiState: State.SQLite.ClientDocumentTableDef<"uiState", Schema.Struct.ReadonlySide<{
readonly newTodoText: Schema.String;
readonly filter: Schema.Literals<readonly ["all", "active", "completed"]>;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly newTodoText: Schema.String;
readonly filter: Schema.Literals<readonly ["all", "active", "completed"]>;
}, "Encoded">, {
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", Struct<Fields extends Struct.Fields>.ReadonlySide<{ readonly newTodoText: String; readonly filter: Literals<readonly ["all", "active", "completed"]>; }, "Type">, Struct.ReadonlySide<...>, { ...; }>.set: State.SQLite.ClientDocumentTableDef.SetEventDefLike<"uiState", Schema.Struct.ReadonlySide<{
readonly newTodoText: Schema.String;
readonly filter: Schema.Literals<readonly ["all", "active", "completed"]>;
}, "Type">, {
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", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Encoded">>>;
"v1.TodoCompleted": State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoCompleted", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">>>;
"v1.TodoUncompleted": State.SQLite.Materializer<...>;
"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", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Encoded">>;
todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">>;
todoUncompleted: State.SQLite.EventDef<...>;
todoDeleted: State.SQLite.EventDef<...>;
todoClearedCompleted: State.SQLite.EventDef<...>;
uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<...>;
}>(_eventDefRecord: {
todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Encoded">>;
todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">>;
todoUncompleted: State.SQLite.EventDef<...>;
todoDeleted: State.SQLite.EventDef<...>;
todoClearedCompleted: State.SQLite.EventDef<...>;
uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<...>;
}, handlers: {
...;
}) => {
...;
}

Builder function for creating a type-safe materializer map.

This is the primary way to define materializers in LiveStore. It ensures:

  • Every non-derived event has a corresponding materializer
  • Materializer argument types match their event schemas
  • Derived events are excluded from the required handlers

@example

import { State } from '@livestore/livestore'
const handlers = State.SQLite.materializers(events, {
// Handler for each event - argument types are inferred
'v1.TodoCreated': ({ id, text, completed }) =>
tables.todos.insert({ id, text, completed }),
'v1.TodoUpdated': ({ id, text }) =>
tables.todos.update({ text }).where({ id }),
// Can return multiple operations
'v1.UserCreatedWithDefaults': ({ userId, name }) => [
tables.users.insert({ id: userId, name }),
tables.settings.insert({ userId, theme: 'light' }),
],
// Can query current state
'v1.TodoToggled': ({ id }, { query }) => {
const todo = query(tables.todos.select().where({ id }).first())
return tables.todos.update({ completed: !todo?.completed }).where({ id })
},
})

materializers
(
const events: {
todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Encoded">>;
todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">>;
todoUncompleted: State.SQLite.EventDef<...>;
todoDeleted: State.SQLite.EventDef<...>;
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.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>;
uiState: State.SQLite.ClientDocumentTableDef<...>;
}
tables
.
todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
todos
.
insert: (values: {
readonly id: string;
readonly text?: string;
readonly completed?: boolean;
readonly deletedAt?: Date | null;
}) => QueryBuilder<readonly Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.Codec<string, string, never, never>;
readonly text: Schema.Codec<string, string, never, never>;
readonly completed: Schema.Codec<boolean, number, never, never>;
readonly deletedAt: Schema.Codec<Date | null, number | null, never, never>;
}, "Type">[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"todos", {
...;
}>, 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' })

@paramvalues - The row values to insert.

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.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>;
uiState: State.SQLite.ClientDocumentTableDef<...>;
}
tables
.
todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
todos
.
update: (values: Partial<Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.Codec<string, string, never, never>;
readonly text: Schema.Codec<string, string, never, never>;
readonly completed: Schema.Codec<boolean, number, never, never>;
readonly deletedAt: Schema.Codec<Date | null, number | null, never, never>;
}, "Type">>) => QueryBuilder<readonly Schema.Struct.ReadonlySide<{
readonly id: Schema.Codec<string, string, never, never>;
readonly text: Schema.Codec<string, string, never, never>;
readonly completed: Schema.Codec<boolean, number, never, never>;
readonly deletedAt: Schema.Codec<Date | null, number | null, never, never>;
}, "Type">[], State.SQLite.TableDefBase<...>, "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: Exclude<QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase<any, any>, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>;
value: string;
} | {
op: QueryBuilder.WhereOps.MultiValue;
value: readonly string[];
} | undefined;
readonly text: string | {
op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>;
value: string;
} | {
...;
} | undefined;
readonly completed: boolean | ... 2 more ... | undefined;
readonly deletedAt: Date | ... 3 more ... | undefined;
}>) => QueryBuilder<...> (+3 overloads)
where
({
id?: string | {
op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>;
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.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>;
uiState: State.SQLite.ClientDocumentTableDef<...>;
}
tables
.
todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
todos
.
update: (values: Partial<Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.Codec<string, string, never, never>;
readonly text: Schema.Codec<string, string, never, never>;
readonly completed: Schema.Codec<boolean, number, never, never>;
readonly deletedAt: Schema.Codec<Date | null, number | null, never, never>;
}, "Type">>) => QueryBuilder<readonly Schema.Struct.ReadonlySide<{
readonly id: Schema.Codec<string, string, never, never>;
readonly text: Schema.Codec<string, string, never, never>;
readonly completed: Schema.Codec<boolean, number, never, never>;
readonly deletedAt: Schema.Codec<Date | null, number | null, never, never>;
}, "Type">[], State.SQLite.TableDefBase<...>, "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: Exclude<QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase<any, any>, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>;
value: string;
} | {
op: QueryBuilder.WhereOps.MultiValue;
value: readonly string[];
} | undefined;
readonly text: string | {
op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>;
value: string;
} | {
...;
} | undefined;
readonly completed: boolean | ... 2 more ... | undefined;
readonly deletedAt: Date | ... 3 more ... | undefined;
}>) => QueryBuilder<...> (+3 overloads)
where
({
id?: string | {
op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>;
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.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>;
uiState: State.SQLite.ClientDocumentTableDef<...>;
}
tables
.
todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
todos
.
update: (values: Partial<Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.Codec<string, string, never, never>;
readonly text: Schema.Codec<string, string, never, never>;
readonly completed: Schema.Codec<boolean, number, never, never>;
readonly deletedAt: Schema.Codec<Date | null, number | null, never, never>;
}, "Type">>) => QueryBuilder<readonly Schema.Struct.ReadonlySide<{
readonly id: Schema.Codec<string, string, never, never>;
readonly text: Schema.Codec<string, string, never, never>;
readonly completed: Schema.Codec<boolean, number, never, never>;
readonly deletedAt: Schema.Codec<Date | null, number | null, never, never>;
}, "Type">[], State.SQLite.TableDefBase<...>, "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: Exclude<QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase<any, any>, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>;
value: string;
} | {
op: QueryBuilder.WhereOps.MultiValue;
value: readonly string[];
} | undefined;
readonly text: string | {
op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>;
value: string;
} | {
...;
} | undefined;
readonly completed: boolean | ... 2 more ... | undefined;
readonly deletedAt: Date | ... 3 more ... | undefined;
}>) => QueryBuilder<...> (+3 overloads)
where
({
id?: string | {
op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>;
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.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>;
uiState: State.SQLite.ClientDocumentTableDef<...>;
}
tables
.
todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
todos
.
update: (values: Partial<Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.Codec<string, string, never, never>;
readonly text: Schema.Codec<string, string, never, never>;
readonly completed: Schema.Codec<boolean, number, never, never>;
readonly deletedAt: Schema.Codec<Date | null, number | null, never, never>;
}, "Type">>) => QueryBuilder<readonly Schema.Struct.ReadonlySide<{
readonly id: Schema.Codec<string, string, never, never>;
readonly text: Schema.Codec<string, string, never, never>;
readonly completed: Schema.Codec<boolean, number, never, never>;
readonly deletedAt: Schema.Codec<Date | null, number | null, never, never>;
}, "Type">[], State.SQLite.TableDefBase<...>, "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: Exclude<QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase<any, any>, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>;
value: string;
} | {
op: QueryBuilder.WhereOps.MultiValue;
value: readonly string[];
} | undefined;
readonly text: string | {
op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>;
value: string;
} | {
...;
} | undefined;
readonly completed: boolean | ... 2 more ... | undefined;
readonly deletedAt: Date | ... 3 more ... | undefined;
}>) => QueryBuilder<...> (+3 overloads)
where
({
completed?: boolean | {
op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>;
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.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>;
uiState: State.SQLite.ClientDocumentTableDef<...>;
};
materializers: {
...;
};
}>(inputSchema: {
tables: {
todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>;
uiState: State.SQLite.ClientDocumentTableDef<...>;
};
materializers: {
...;
};
}) => InternalState
makeState
({
tables: {
todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly text: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly completed: {
columnType: "integer";
schema: Schema.Codec<boolean, number, never, never>;
default: Some<false>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly deletedAt: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>;
uiState: State.SQLite.ClientDocumentTableDef<...>;
}
tables
,
materializers: {
"v1.TodoCreated": State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Encoded">>>;
"v1.TodoCompleted": State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoCompleted", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">>>;
"v1.TodoUncompleted": State.SQLite.Materializer<...>;
"v1.TodoDeleted": State.SQLite.Materializer<...>;
"v1.TodoClearedCompleted": State.SQLite.Materializer<...>;
}
materializers
})
export const
const schema: FromInputSchema.DeriveSchema<{
events: {
todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Encoded">>;
todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">>;
todoUncompleted: State.SQLite.EventDef<...>;
todoDeleted: State.SQLite.EventDef<...>;
todoClearedCompleted: State.SQLite.EventDef<...>;
uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<...>;
};
state: InternalState;
}>
schema
=
makeSchema<{
events: {
todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Encoded">>;
todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">>;
todoUncompleted: State.SQLite.EventDef<...>;
todoDeleted: State.SQLite.EventDef<...>;
todoClearedCompleted: State.SQLite.EventDef<...>;
uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<...>;
};
state: InternalState;
}>(inputSchema: {
events: {
todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Encoded">>;
todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">>;
todoUncompleted: State.SQLite.EventDef<...>;
todoDeleted: State.SQLite.EventDef<...>;
todoClearedCompleted: State.SQLite.EventDef<...>;
uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<...>;
};
state: InternalState;
}): FromInputSchema.DeriveSchema<...>
makeSchema
({
events: {
todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Encoded">>;
todoCompleted: State.SQLite.EventDef<"v1.TodoCompleted", Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
}, "Encoded">>;
todoUncompleted: State.SQLite.EventDef<...>;
todoDeleted: State.SQLite.EventDef<...>;
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: Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly email: {
columnType: "text";
schema: Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly age: {
...;
};
readonly isActive: {
...;
};
readonly metadata: {
...;
};
}>, State.SQLite.WithDefaults<...>, Struct<...>>
userTable
=
import State
State
.
import SQLite
SQLite
.
function table<"users", {
readonly id: {
columnType: "text";
schema: Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly email: {
columnType: "text";
schema: Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly age: {
...;
};
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.annotate({ 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,
}).annotate({ 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: Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly email: {
columnType: "text";
schema: Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly age: {
...;
};
readonly isActive: {
...;
};
readonly metadata: {
...;
};
}
columns
: {
id: {
columnType: "text";
schema: Codec<string, string, never, 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?: Codec<string, string, never, never>;
default?: typeof NoDefault;
nullable?: false;
primaryKey?: true;
autoIncrement?: false;
}) => {
columnType: "text";
schema: Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
} (+1 overload)
text
({
primaryKey?: true
primaryKey
: true }),
email: {
columnType: "text";
schema: Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
email
:
import State
State
.
import SQLite
SQLite
.
const text: () => {
columnType: "text";
schema: Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
text
(),
name: {
columnType: "text";
schema: Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
name
:
import State
State
.
import SQLite
SQLite
.
const text: () => {
columnType: "text";
schema: Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
text
(),
age: {
columnType: "integer";
schema: Codec<number, number, never, 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?: Codec<number, number, never, never>;
default?: 0;
nullable?: false;
primaryKey?: false;
autoIncrement?: false;
}) => {
columnType: "integer";
schema: Codec<number, number, never, never>;
default: Some<0>;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
integer
({
default?: 0
default
: 0 }),
isActive: {
columnType: "integer";
schema: Codec<boolean, number, never, 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: Codec<boolean, number, never, never>;
default: Some<true>;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
boolean
({
default?: true
default
: true }),
metadata: {
columnType: "text";
schema: Codec<unknown, string | null, never, 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?: Codec<unknown, any, never, never>;
default?: any;
nullable?: true;
primaryKey?: false;
autoIncrement?: false;
}) => {
columnType: "text";
schema: Codec<unknown, string | null, never, 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<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}>
UserMetadata
=
import Schema
Schema
.
function Struct<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}>(fields: {
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}): Schema.Struct<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}>

Defines a struct schema from a map of field schemas.

Details

Each field value is a schema. Use

optionalKey

or

optional

to mark fields as optional, and

mutableKey

to mark them as mutable.

The resulting schema's Type is a readonly object type with the fields' decoded types. The Encoded form mirrors the field schemas' encoded types.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
const alice = Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 })
console.log(alice)
// { name: 'Alice', age: 30 }

@since3.10.0

Struct
({
petName: Schema.String
petName
:
import Schema
Schema
.
const String: Schema.String

Type-level representation of

String

.

Schema for string values. Validates that the input is typeof "string".

@since4.0.0

@since4.0.0

String
,
favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>
favoriteColor
:
import Schema
Schema
.
function Literals<readonly ["red", "blue", "green"]>(literals: readonly ["red", "blue", "green"]): Schema.Literals<readonly ["red", "blue", "green"]>

Creates a union schema from an array of literal values.

Example (Defining status codes)

import { Schema } from "effect"
const schema = Schema.Literals(["active", "inactive", "pending"])
// accepts "active", "inactive", or "pending"

@seeLiteral for a schema that represents a single literal.

@since4.0.0

Literals
(['red', 'blue', 'green']),
})
export const
const userTable: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"user", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly metadata: {
columnType: "text";
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}, "Type">, string, never, never>;
default: Some<...> | None<...>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
userTable
=
import State
State
.
import SQLite
SQLite
.
function table<"user", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly metadata: {
columnType: "text";
schema: Schema.Codec<Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}, "Type">, string, never, never>;
default: Some<...> | 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.annotate({ 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,
}).annotate({ 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.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly metadata: {
columnType: "text";
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}, "Type">, string, never, never>;
default: Some<...> | None<...>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}
columns
: {
id: {
columnType: "text";
schema: Schema.Codec<string, string, never, 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.Codec<string, string, never, never>;
default?: typeof NoDefault;
nullable?: false;
primaryKey?: true;
autoIncrement?: false;
}) => {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
} (+1 overload)
text
({
primaryKey?: true
primaryKey
: true }),
name: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
name
:
import State
State
.
import SQLite
SQLite
.
const text: () => {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
text
(),
metadata: {
columnType: "text";
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}, "Type">, string, never, never>;
default: Some<any> | None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
metadata
:
import State
State
.
import SQLite
SQLite
.
const json: <Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}, "Type">, false, any, false, false>(args: {
schema?: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}, "Type">, any, never, never>;
default?: any;
nullable?: false;
primaryKey?: false;
autoIncrement?: false;
}) => {
columnType: "text";
... 4 more ...;
autoIncrement: false;
} (+1 overload)
json
({
schema?: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}, "Type">, any, never, never>
schema
:
const UserMetadata: Schema.Struct<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}>
UserMetadata
}),
},
})

LiveStore automatically migrates the database to the newest schema and rematerializes state from the eventlog.

  • 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
(alias) namespace React
import React
React
from 'react'
import type {
class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>

Central interface to a LiveStore database providing reactive queries, event commits, and sync.

A Store instance wraps a local SQLite database that is kept in sync with other clients via an event log. Instead of mutating state directly, you commit events that get materialized into database rows. Queries automatically re-run when their underlying tables change.

Creating a Store

Use createStore (Effect-based) or createStorePromise to obtain a Store instance. In React applications, use StoreRegistry with <StoreRegistryProvider> and the useStore() hook which manages the Store lifecycle.

Querying Data

Use

Store.query

for one-shot reads or

Store.subscribe

for reactive subscriptions. Both accept query builders (e.g. tables.todo.where({ complete: true })) or custom LiveQueryDefs.

Committing Events

Use

Store.commit

to persist events. Events are immediately materialized locally and asynchronously synced to other clients. Multiple events can be committed atomically.

Lifecycle

The Store must be shut down when no longer needed via

Store.shutdown

or

Store.shutdownPromise

. Framework integrations (React, Effect) handle this automatically.

@example

// Query data
const todos = store.query(tables.todo.where({ complete: false }))
// Subscribe to changes
const unsubscribe = store.subscribe(tables.todo.all(), (todos) => {
console.log('Todos updated:', todos)
})
// Commit an event
store.commit(events.todoCreated({ id: nanoid(), text: 'Buy milk' }))

Store
} from '@livestore/livestore'
import {
import tables
tables
} from '../../../framework-integrations/react/schema.ts'
import {
import useAppStore
useAppStore
} from '../../../framework-integrations/react/store.ts'
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 = {}>

Central interface to a LiveStore database providing reactive queries, event commits, and sync.

A Store instance wraps a local SQLite database that is kept in sync with other clients via an event log. Instead of mutating state directly, you commit events that get materialized into database rows. Queries automatically re-run when their underlying tables change.

Creating a Store

Use createStore (Effect-based) or createStorePromise to obtain a Store instance. In React applications, use StoreRegistry with <StoreRegistryProvider> and the useStore() hook which manages the Store lifecycle.

Querying Data

Use

Store.query

for one-shot reads or

Store.subscribe

for reactive subscriptions. Both accept query builders (e.g. tables.todo.where({ complete: true })) or custom LiveQueryDefs.

Committing Events

Use

Store.commit

to persist events. Events are immediately materialized locally and asynchronously synced to other clients. Multiple events can be committed atomically.

Lifecycle

The Store must be shut down when no longer needed via

Store.shutdown

or

Store.shutdownPromise

. Framework integrations (React, Effect) handle this automatically.

@example

// Query data
const todos = store.query(tables.todo.where({ complete: false }))
// Subscribe to changes
const unsubscribe = store.subscribe(tables.todo.all(), (todos) => {
console.log('Todos updated:', todos)
})
// Commit an event
store.commit(events.todoCreated({ id: nanoid(), text: 'Buy milk' }))

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: <{
newTodoText: string;
filter: "all" | "active" | "completed";
}>(query: Queryable<{
newTodoText: string;
filter: "all" | "active" | "completed";
}> | {
query: string;
bindValues: Bindable;
schema?: Decoder<{
newTodoText: string;
filter: "all" | "active" | "completed";
}, never>;
}, options?: {
otelContext?: Context;
debugRefreshReason?: RefreshReason;
}) => {
newTodoText: string;
filter: "all" | "active" | "completed";
}

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
(
import tables
tables
.
any
uiState
.
any
get
())
export const
const setNewTodoText: (store: Store, newTodoText: string) => void
setNewTodoText
= (
store: Store<LiveStoreSchema.Any, {}>
store
:
class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>

Central interface to a LiveStore database providing reactive queries, event commits, and sync.

A Store instance wraps a local SQLite database that is kept in sync with other clients via an event log. Instead of mutating state directly, you commit events that get materialized into database rows. Queries automatically re-run when their underlying tables change.

Creating a Store

Use createStore (Effect-based) or createStorePromise to obtain a Store instance. In React applications, use StoreRegistry with <StoreRegistryProvider> and the useStore() hook which manages the Store lifecycle.

Querying Data

Use

Store.query

for one-shot reads or

Store.subscribe

for reactive subscriptions. Both accept query builders (e.g. tables.todo.where({ complete: true })) or custom LiveQueryDefs.

Committing Events

Use

Store.commit

to persist events. Events are immediately materialized locally and asynchronously synced to other clients. Multiple events can be committed atomically.

Lifecycle

The Store must be shut down when no longer needed via

Store.shutdown

or

Store.shutdownPromise

. Framework integrations (React, Effect) handle this automatically.

@example

// Query data
const todos = store.query(tables.todo.where({ complete: false }))
// Subscribe to changes
const unsubscribe = store.subscribe(tables.todo.all(), (todos) => {
console.log('Todos updated:', todos)
})
// Commit an event
store.commit(events.todoCreated({ id: nanoid(), text: 'Buy milk' }))

Store
,
newTodoText: string
newTodoText
: string): void => {
store: Store<LiveStoreSchema.Any, {}>
store
.
Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.commit: <readonly [any]>(list_0: any) => void (+3 overloads)
commit
(
import tables
tables
.
any
uiState
.
any
set
({
newTodoText: string
newTodoText
}))
}
export const
const UiStateFilter: React.FC<{}>
UiStateFilter
:
(alias) namespace React
import React
React
.
type 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 store: any
store
=
import useAppStore
useAppStore
()
const [
const state: any
state
,
const setState: any
setState
] =
const store: any
store
.
any
useClientDocument
(
import tables
tables
.
any
uiState
)
const
const showActive: () => void
showActive
=
(alias) namespace React
import React
React
.
function 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: any
setState
({
filter: string
filter
: 'active' })
}, [
const setState: any
setState
])
const
const showAll: () => void
showAll
=
(alias) namespace React
import React
React
.
function 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: any
setState
({
filter: string
filter
: 'all' })
}, [
const setState: any
setState
])
return (
<
JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
<
JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
ButtonHTMLAttributes<HTMLButtonElement>.type?: "button" | "submit" | "reset" | undefined
type
="button"
DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
onClick
={
const showAll: () => void
showAll
}>
All
</
JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>
<
JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
ButtonHTMLAttributes<HTMLButtonElement>.type?: "button" | "submit" | "reset" | undefined
type
="button"
DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
onClick
={
const showActive: () => void
showActive
}>
Active ({
const state: any
state
.
any
filter
=== 'active' ? 'selected' : 'select'})
</
JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>
</
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 { type
type FC<P = {}> = 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
,
function useCallback<T extends Function>(callback: T, deps: DependencyList): T

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

useCallback
} from 'react'
import {
import Schema
Schema
,
import State
State
, type
class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>

Central interface to a LiveStore database providing reactive queries, event commits, and sync.

A Store instance wraps a local SQLite database that is kept in sync with other clients via an event log. Instead of mutating state directly, you commit events that get materialized into database rows. Queries automatically re-run when their underlying tables change.

Creating a Store

Use createStore (Effect-based) or createStorePromise to obtain a Store instance. In React applications, use StoreRegistry with <StoreRegistryProvider> and the useStore() hook which manages the Store lifecycle.

Querying Data

Use

Store.query

for one-shot reads or

Store.subscribe

for reactive subscriptions. Both accept query builders (e.g. tables.todo.where({ complete: true })) or custom LiveQueryDefs.

Committing Events

Use

Store.commit

to persist events. Events are immediately materialized locally and asynchronously synced to other clients. Multiple events can be committed atomically.

Lifecycle

The Store must be shut down when no longer needed via

Store.shutdown

or

Store.shutdownPromise

. Framework integrations (React, Effect) handle this automatically.

@example

// Query data
const todos = store.query(tables.todo.where({ complete: false }))
// Subscribe to changes
const unsubscribe = store.subscribe(tables.todo.all(), (todos) => {
console.log('Todos updated:', todos)
})
// Commit an event
store.commit(events.todoCreated({ id: nanoid(), text: 'Buy milk' }))

Store
} from '@livestore/livestore'
import {
import useAppStore
useAppStore
} from '../../../framework-integrations/react/store.ts'
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: Schema.Any;
readonly default: {
readonly value: null;
};
}>({ name, schema: valueSchema, ...inputOptions }: {
name: "Kv";
schema: Schema.Codec<any, any, never, never>;
} & {
readonly name: "Kv";
readonly schema: 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.Codec<any, any, never, never> & Schema.Any
schema
:
import Schema
Schema
.
const Any: Schema.Any

Type-level representation of

Any

.

Schema for the any type. Accepts any value without validation.

@since3.10.0

@seeUnknown for a safer alternative that uses unknown.

@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 = {}>

Central interface to a LiveStore database providing reactive queries, event commits, and sync.

A Store instance wraps a local SQLite database that is kept in sync with other clients via an event log. Instead of mutating state directly, you commit events that get materialized into database rows. Queries automatically re-run when their underlying tables change.

Creating a Store

Use createStore (Effect-based) or createStorePromise to obtain a Store instance. In React applications, use StoreRegistry with <StoreRegistryProvider> and the useStore() hook which manages the Store lifecycle.

Querying Data

Use

Store.query

for one-shot reads or

Store.subscribe

for reactive subscriptions. Both accept query builders (e.g. tables.todo.where({ complete: true })) or custom LiveQueryDefs.

Committing Events

Use

Store.commit

to persist events. Events are immediately materialized locally and asynchronously synced to other clients. Multiple events can be committed atomically.

Lifecycle

The Store must be shut down when no longer needed via

Store.shutdown

or

Store.shutdownPromise

. Framework integrations (React, Effect) handle this automatically.

@example

// Query data
const todos = store.query(tables.todo.where({ complete: false }))
// Subscribe to changes
const unsubscribe = store.subscribe(tables.todo.all(), (todos) => {
console.log('Todos updated:', todos)
})
// Commit an event
store.commit(events.todoCreated({ id: nanoid(), text: 'Buy milk' }))

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.Decoder<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<any, any>, 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 = {}>

Central interface to a LiveStore database providing reactive queries, event commits, and sync.

A Store instance wraps a local SQLite database that is kept in sync with other clients via an event log. Instead of mutating state directly, you commit events that get materialized into database rows. Queries automatically re-run when their underlying tables change.

Creating a Store

Use createStore (Effect-based) or createStorePromise to obtain a Store instance. In React applications, use StoreRegistry with <StoreRegistryProvider> and the useStore() hook which manages the Store lifecycle.

Querying Data

Use

Store.query

for one-shot reads or

Store.subscribe

for reactive subscriptions. Both accept query builders (e.g. tables.todo.where({ complete: true })) or custom LiveQueryDefs.

Committing Events

Use

Store.commit

to persist events. Events are immediately materialized locally and asynchronously synced to other clients. Multiple events can be committed atomically.

Lifecycle

The Store must be shut down when no longer needed via

Store.shutdown

or

Store.shutdownPromise

. Framework integrations (React, Effect) handle this automatically.

@example

// Query data
const todos = store.query(tables.todo.where({ complete: false }))
// Subscribe to changes
const unsubscribe = store.subscribe(tables.todo.all(), (todos) => {
console.log('Todos updated:', todos)
})
// Commit an event
store.commit(events.todoCreated({ id: nanoid(), text: 'Buy milk' }))

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: FC<{
id: string;
}>
KvViewer
:
type FC<P = {}> = 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 store: any
store
=
import useAppStore
useAppStore
()
const [
const value: any
value
,
const setValue: any
setValue
] =
const store: any
store
.
any
useClientDocument
(
const kv: State.SQLite.ClientDocumentTableDef<"Kv", any, any, {
partialSet: false;
default: {
id: undefined;
value: null;
};
}>
kv
,
id: string
id
)
const
const handleClick: () => void
handleClick
=
useCallback<() => void>(callback: () => void, deps: DependencyList): () => void

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

useCallback
(() => {
const setValue: any
setValue
('hello')
}, [
const setValue: any
setValue
])
return (
<
JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
ButtonHTMLAttributes<HTMLButtonElement>.type?: "button" | "submit" | "reset" | undefined
type
="button"
DOMAttributes<HTMLButtonElement>.onClick?: MouseEventHandler<HTMLButtonElement> | undefined
onClick
={
const handleClick: () => void
handleClick
}>
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
)}
</
JSX.IntrinsicElements.button: DetailedHTMLProps<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<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}>
UserMetadata
=
import Schema
Schema
.
function Struct<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}>(fields: {
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}): Schema.Struct<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}>

Defines a struct schema from a map of field schemas.

Details

Each field value is a schema. Use

optionalKey

or

optional

to mark fields as optional, and

mutableKey

to mark them as mutable.

The resulting schema's Type is a readonly object type with the fields' decoded types. The Encoded form mirrors the field schemas' encoded types.

Example (Defining a basic struct)

import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
const alice = Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 })
console.log(alice)
// { name: 'Alice', age: 30 }

@since3.10.0

Struct
({
petName: Schema.String
petName
:
import Schema
Schema
.
const String: Schema.String

Type-level representation of

String

.

Schema for string values. Validates that the input is typeof "string".

@since4.0.0

@since4.0.0

String
,
favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>
favoriteColor
:
import Schema
Schema
.
function Literals<readonly ["red", "blue", "green"]>(literals: readonly ["red", "blue", "green"]): Schema.Literals<readonly ["red", "blue", "green"]>

Creates a union schema from an array of literal values.

Example (Defining status codes)

import { Schema } from "effect"
const schema = Schema.Literals(["active", "inactive", "pending"])
// accepts "active", "inactive", or "pending"

@seeLiteral for a schema that represents a single literal.

@since4.0.0

Literals
(['red', 'blue', 'green']),
})
export const
const userTable: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"user", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly metadata: {
columnType: "text";
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}, "Type">, string, never, never>;
default: Some<...> | None<...>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
userTable
=
import State
State
.
import SQLite
SQLite
.
function table<"user", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly metadata: {
columnType: "text";
schema: Schema.Codec<Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}, "Type">, string, never, never>;
default: Some<...> | 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.annotate({ 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,
}).annotate({ 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.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly name: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
readonly metadata: {
columnType: "text";
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}, "Type">, string, never, never>;
default: Some<...> | None<...>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}
columns
: {
id: {
columnType: "text";
schema: Schema.Codec<string, string, never, 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.Codec<string, string, never, never>;
default?: typeof NoDefault;
nullable?: false;
primaryKey?: true;
autoIncrement?: false;
}) => {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
} (+1 overload)
text
({
primaryKey?: true
primaryKey
: true }),
name: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
name
:
import State
State
.
import SQLite
SQLite
.
const text: () => {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
text
(),
metadata: {
columnType: "text";
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}, "Type">, string, never, never>;
default: Some<any> | None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
metadata
:
import State
State
.
import SQLite
SQLite
.
const json: <Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}, "Type">, false, any, false, false>(args: {
schema?: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}, "Type">, any, never, never>;
default?: any;
nullable?: false;
primaryKey?: false;
autoIncrement?: false;
}) => {
columnType: "text";
... 4 more ...;
autoIncrement: false;
} (+1 overload)
json
({
schema?: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["red", "blue", "green"]>;
}, "Type">, any, never, never>
schema
:
const UserMetadata: Schema.Struct<{
readonly petName: Schema.String;
readonly favoriteColor: Schema.Literals<readonly ["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