Skip to content

Materializers

Materializers are functions that allow you to write to your database in response to events. Materializers are executed in the order of the events in the eventlog.

import {
const defineMaterializer: <TEventDef extends State.SQLite.EventDef.AnyWithoutFn>(_eventDef: TEventDef, materializer: State.SQLite.Materializer<TEventDef>) => State.SQLite.Materializer<TEventDef>

Type-safe wrapper for defining a single materializer.

Useful when defining materializers separately from the materializers() builder. The first argument provides type inference for the second.

@example

const todoCreatedHandler = defineMaterializer(
events.todoCreated,
({ id, text }) => tables.todos.insert({ id, text, completed: false })
)

defineMaterializer
,
import Events
Events
,
import Schema
Schema
,
import State
State
} from '@livestore/livestore'
export const
const 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: None<never>;
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 previousIds: {
...;
};
}>, 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: None<never>;
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 previousIds: {
...;
};
}, 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: None<never>;
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 previousIds: {
...;
};
}
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: None<never>;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
text
:
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
(),
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 }),
previousIds: {
columnType: "text";
schema: Schema.Codec<readonly string[] | null, string | null, never, never>;
default: Some<any> | None<never>;
nullable: true;
primaryKey: false;
autoIncrement: false;
}
previousIds
:
import State
State
.
import SQLite
SQLite
.
const json: <readonly string[], true, any, false, false>(args: {
schema?: Schema.Codec<readonly string[], any, never, never>;
default?: any;
nullable?: true;
primaryKey?: false;
autoIncrement?: false;
}) => {
columnType: "text";
schema: Schema.Codec<readonly string[] | null, string | null, never, never>;
default: Some<any> | None<never>;
nullable: true;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
json
({
schema?: Schema.Codec<readonly string[], any, never, never>
schema
:
import Schema
Schema
.
Array<Schema.String>(self: Schema.String): Schema.$Array<Schema.String>
export Array

Defines a ReadonlyArray schema for a given element schema.

Example (Defining an array of strings)

import { Schema } from "effect"
const schema = Schema.Array(Schema.String)
const result = Schema.decodeUnknownSync(schema)(["a", "b", "c"])
console.log(result)
// [ 'a', 'b', 'c' ]

@since4.0.0

Array
(
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
),
nullable?: true
nullable
: true,
}),
},
})
export const
const table1: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"settings", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly someVal: {
columnType: "integer";
schema: Schema.Codec<number, number, never, never>;
default: Some<0>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}>, State.SQLite.WithDefaults<{
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly someVal: {
columnType: "integer";
schema: Schema.Codec<number, number, never, never>;
default: Some<0>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}>, Schema.Struct<...>>
table1
=
import State
State
.
import SQLite
SQLite
.
function table<"settings", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly someVal: {
columnType: "integer";
schema: Schema.Codec<number, number, never, never>;
default: Some<0>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}, Partial<{
indexes: Index[];
}>>(args: {
name: "settings";
columns: {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly someVal: {
columnType: "integer";
schema: Schema.Codec<number, number, never, never>;
default: Some<0>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
};
} & 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: "settings"
name
: 'settings',
columns: {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly someVal: {
columnType: "integer";
schema: Schema.Codec<number, number, never, never>;
default: Some<0>;
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 }),
someVal: {
columnType: "integer";
schema: Schema.Codec<number, number, never, never>;
default: Some<0>;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
someVal
:
import State
State
.
import SQLite
SQLite
.
const integer: <number, number, false, 0, false, false>(args: {
schema?: Schema.Codec<number, number, never, never>;
default?: 0;
nullable?: false;
primaryKey?: false;
autoIncrement?: false;
}) => {
columnType: "integer";
schema: Schema.Codec<number, number, never, never>;
default: Some<0>;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
integer
({
default?: 0
default
: 0 }),
},
})
export const
const table2: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"preferences", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly otherVal: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"default">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}>, State.SQLite.WithDefaults<{
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly otherVal: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"default">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}>, Schema.Struct<...>>
table2
=
import State
State
.
import SQLite
SQLite
.
function table<"preferences", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly otherVal: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"default">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}, Partial<{
indexes: Index[];
}>>(args: {
name: "preferences";
columns: {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly otherVal: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"default">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
};
} & 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: "preferences"
name
: 'preferences',
columns: {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly otherVal: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"default">;
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 }),
otherVal: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"default">;
nullable: false;
primaryKey: false;
autoIncrement: false;
}
otherVal
:
import State
State
.
import SQLite
SQLite
.
const text: <string, string, false, "default", false, false>(args: {
schema?: Schema.Codec<string, string, never, never>;
default?: "default";
nullable?: false;
primaryKey?: false;
autoIncrement?: false;
}) => {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"default">;
nullable: false;
primaryKey: false;
autoIncrement: false;
} (+1 overload)
text
({
default?: "default"
default
: 'default' }),
},
})
export const
const events: {
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>;
readonly userPreferencesUpdated: State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>;
readonly factoryResetApplied: State.SQLite.EventDef<...>;
}
events
= {
todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>
todoCreated
:
import Events
Events
.
synced<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>(args: {
name: "todoCreated";
schema: Schema.Codec<{
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, never, never>;
} & Omit<State.SQLite.DefineEventOptions<{
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, 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: "todoCreated"
name
: 'todoCreated',
schema: Schema.Codec<{
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, never, never>
schema
:
import Schema
Schema
.
function Struct<{
readonly id: Schema.String;
readonly text: Schema.String;
readonly completed: Schema.optional<Schema.Boolean>;
}>(fields: {
readonly id: Schema.String;
readonly text: Schema.String;
readonly completed: Schema.optional<Schema.Boolean>;
}): Schema.Struct<{
readonly id: Schema.String;
readonly text: Schema.String;
readonly completed: Schema.optional<Schema.Boolean>;
}>

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
,
completed: Schema.optional<Schema.Boolean>
completed
:
import Schema
Schema
.
const Boolean: Schema.Boolean

Type-level representation of

Boolean

.

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

When to use

Use to validate values that are already JavaScript booleans.

@since4.0.0

@seeBooleanFromBit for a schema that decodes bit literals 0 or 1 into a boolean

@since4.0.0

Boolean
.
Pipeable.pipe<Schema.Boolean, Schema.optional<Schema.Boolean>>(this: Schema.Boolean, ab: (_: Schema.Boolean) => Schema.optional<Schema.Boolean>): Schema.optional<Schema.Boolean> (+21 overloads)
pipe
(
import Schema
Schema
.
const optional: optionalLambda

Type-level representation returned by

optional

.

Marks a struct field as optional, allowing the key to be absent or undefined.

Details

The resulting property may be absent or explicitly set to undefined. Equivalent to optionalKey(UndefinedOr(S)).

Use

optionalKey

instead if you want exact optional semantics (absent only, not undefined).

Example (Defining an optional field accepting undefined)

import { Schema } from "effect"
const schema = Schema.Struct({
name: Schema.String,
age: Schema.optional(Schema.Number)
})
// { readonly name: string; readonly age?: number | undefined }
type Person = typeof schema.Type

@since3.10.0

@since3.10.0

optional
),
}),
}),
userPreferencesUpdated: State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>
userPreferencesUpdated
:
import Events
Events
.
synced<"userPreferencesUpdated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>(args: {
name: "userPreferencesUpdated";
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: 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: "userPreferencesUpdated"
name
: 'userPreferencesUpdated',
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">, never, never>
schema
:
import Schema
Schema
.
function Struct<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}>(fields: {
readonly userId: Schema.String;
readonly theme: Schema.String;
}): Schema.Struct<{
readonly userId: Schema.String;
readonly theme: 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
({
userId: Schema.String
userId
:
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
,
theme: Schema.String
theme
:
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
}),
}),
factoryResetApplied: State.SQLite.EventDef<"factoryResetApplied", Schema.Struct.ReadonlySide<{}, "Type">, Schema.Struct.ReadonlySide<{}, "Encoded">>
factoryResetApplied
:
import Events
Events
.
synced<"factoryResetApplied", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{}, "Type">, Schema.Struct.ReadonlySide<{}, "Encoded">>(args: {
name: "factoryResetApplied";
schema: Schema.Codec<Schema.Struct.ReadonlySide<{}, "Type">, Schema.Struct.ReadonlySide<{}, "Encoded">, never, never>;
} & Omit<State.SQLite.DefineEventOptions<Schema.Struct.ReadonlySide<{}, "Type">, false>, "derived" | "clientOnly">): State.SQLite.EventDef<"factoryResetApplied", Schema.Struct.ReadonlySide<{}, "Type">, Schema.Struct.ReadonlySide<...>>
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: "factoryResetApplied"
name
: 'factoryResetApplied',
schema: Schema.Codec<Schema.Struct.ReadonlySide<{}, "Type">, Schema.Struct.ReadonlySide<{}, "Encoded">, never, never>
schema
:
import Schema
Schema
.
function Struct<{}>(fields: {}): Schema.Struct<{}>

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
({}),
}),
} as
type const = {
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>;
readonly userPreferencesUpdated: State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>;
readonly factoryResetApplied: State.SQLite.EventDef<...>;
}
const
export const
const materializers: {
todoCreated: State.SQLite.Materializer<State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>>;
userPreferencesUpdated: State.SQLite.Materializer<State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>>;
factoryResetApplied: State.SQLite.Materializer<...>;
}
materializers
=
import State
State
.
import SQLite
SQLite
.
const materializers: <{
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>;
readonly userPreferencesUpdated: State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>;
readonly factoryResetApplied: State.SQLite.EventDef<...>;
}>(_eventDefRecord: {
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>;
readonly userPreferencesUpdated: State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>;
readonly factoryResetApplied: State.SQLite.EventDef<...>;
}, 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: {
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>;
readonly userPreferencesUpdated: State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>;
readonly factoryResetApplied: State.SQLite.EventDef<...>;
}
events
, {
[
const events: {
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>;
readonly userPreferencesUpdated: State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>;
readonly factoryResetApplied: State.SQLite.EventDef<...>;
}
events
.
todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>
todoCreated
.
name: "todoCreated"

Unique identifier for this event type. Conventionally versioned (e.g., v1.TodoCreated).

name
]:
defineMaterializer<State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>>(_eventDef: State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>, materializer: State.SQLite.Materializer<State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>>): State.SQLite.Materializer<...>

Type-safe wrapper for defining a single materializer.

Useful when defining materializers separately from the materializers() builder. The first argument provides type inference for the second.

@example

const todoCreatedHandler = defineMaterializer(
events.todoCreated,
({ id, text }) => tables.todos.insert({ id, text, completed: false })
)

defineMaterializer
(
const events: {
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>;
readonly userPreferencesUpdated: State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>;
readonly factoryResetApplied: State.SQLite.EventDef<...>;
}
events
.
todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>
todoCreated
, ({
id: string
id
,
text: string
text
,
completed: boolean | undefined
completed
}) =>
const 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: None<never>;
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 previousIds: {
...;
};
}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
todos
.
insert: (values: {
readonly text: string;
readonly id: string;
readonly completed?: boolean;
readonly previousIds?: readonly string[] | 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 previousIds: Schema.Codec<readonly string[] | null, string | 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
:
completed: boolean | undefined
completed
?? false }),
),
[
const events: {
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>;
readonly userPreferencesUpdated: State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>;
readonly factoryResetApplied: State.SQLite.EventDef<...>;
}
events
.
userPreferencesUpdated: State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>
userPreferencesUpdated
.
name: "userPreferencesUpdated"

Unique identifier for this event type. Conventionally versioned (e.g., v1.TodoCreated).

name
]:
defineMaterializer<State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>>(_eventDef: State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>, materializer: State.SQLite.Materializer<...>): State.SQLite.Materializer<...>

Type-safe wrapper for defining a single materializer.

Useful when defining materializers separately from the materializers() builder. The first argument provides type inference for the second.

@example

const todoCreatedHandler = defineMaterializer(
events.todoCreated,
({ id, text }) => tables.todos.insert({ id, text, completed: false })
)

defineMaterializer
(
const events: {
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>;
readonly userPreferencesUpdated: State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>;
readonly factoryResetApplied: State.SQLite.EventDef<...>;
}
events
.
userPreferencesUpdated: State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>
userPreferencesUpdated
, ({
userId: string
userId
,
theme: string
theme
}) => {
var console: Console
console
.
Console.log(...data: any[]): void (+2 overloads)

The console.log() static method outputs a message to the console.

MDN Reference

log
(`User ${
userId: string
userId
} updated theme to ${
theme: string
theme
}.`)
return []
}),
[
const events: {
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>;
readonly userPreferencesUpdated: State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>;
readonly factoryResetApplied: State.SQLite.EventDef<...>;
}
events
.
factoryResetApplied: State.SQLite.EventDef<"factoryResetApplied", Schema.Struct.ReadonlySide<{}, "Type">, Schema.Struct.ReadonlySide<{}, "Encoded">>
factoryResetApplied
.
name: "factoryResetApplied"

Unique identifier for this event type. Conventionally versioned (e.g., v1.TodoCreated).

name
]:
defineMaterializer<State.SQLite.EventDef<"factoryResetApplied", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{}, "Type">, Schema.Struct.ReadonlySide<{}, "Encoded">>>(_eventDef: State.SQLite.EventDef<"factoryResetApplied", Schema.Struct.ReadonlySide<{}, "Type">, Schema.Struct.ReadonlySide<{}, "Encoded">>, materializer: State.SQLite.Materializer<State.SQLite.EventDef<"factoryResetApplied", Schema.Struct.ReadonlySide<{}, "Type">, Schema.Struct.ReadonlySide<{}, "Encoded">>>): State.SQLite.Materializer<...>

Type-safe wrapper for defining a single materializer.

Useful when defining materializers separately from the materializers() builder. The first argument provides type inference for the second.

@example

const todoCreatedHandler = defineMaterializer(
events.todoCreated,
({ id, text }) => tables.todos.insert({ id, text, completed: false })
)

defineMaterializer
(
const events: {
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}, {
readonly text: string;
readonly id: string;
readonly completed?: boolean | undefined;
}>;
readonly userPreferencesUpdated: State.SQLite.EventDef<"userPreferencesUpdated", Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly userId: Schema.String;
readonly theme: Schema.String;
}, "Encoded">>;
readonly factoryResetApplied: State.SQLite.EventDef<...>;
}
events
.
factoryResetApplied: State.SQLite.EventDef<"factoryResetApplied", Schema.Struct.ReadonlySide<{}, "Type">, Schema.Struct.ReadonlySide<{}, "Encoded">>
factoryResetApplied
, () => [
const table1: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"settings", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly someVal: {
columnType: "integer";
schema: Schema.Codec<number, number, never, never>;
default: Some<0>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}>, State.SQLite.WithDefaults<{
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly someVal: {
columnType: "integer";
schema: Schema.Codec<number, number, never, never>;
default: Some<0>;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}>, Schema.Struct<...>>
table1
.
update: (values: Partial<Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.Codec<string, string, never, never>;
readonly someVal: Schema.Codec<number, number, never, never>;
}, "Type">>) => QueryBuilder<readonly Schema.Struct.ReadonlySide<{
readonly id: Schema.Codec<string, string, never, never>;
readonly someVal: Schema.Codec<number, number, never, never>;
}, "Type">[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"settings", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<...>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly someVal: {
...;
};
}>, State.SQLite.WithDefaults<...>>, "select" | ... 6 more ... | "row">

Update rows in the table that match the where clause

Example:

db.todos.update({ status: 'completed' }).where({ id: '123' })

update
({
someVal?: number
someVal
: 0 }),
const table2: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"preferences", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly otherVal: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"default">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}>, State.SQLite.WithDefaults<{
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<never>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly otherVal: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: Some<"default">;
nullable: false;
primaryKey: false;
autoIncrement: false;
};
}>, Schema.Struct<...>>
table2
.
update: (values: Partial<Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.Codec<string, string, never, never>;
readonly otherVal: Schema.Codec<string, string, never, never>;
}, "Type">>) => QueryBuilder<readonly Schema.Struct.ReadonlySide<{
readonly id: Schema.Codec<string, string, never, never>;
readonly otherVal: Schema.Codec<string, string, never, never>;
}, "Type">[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"preferences", {
readonly id: {
columnType: "text";
schema: Schema.Codec<string, string, never, never>;
default: None<...>;
nullable: false;
primaryKey: true;
autoIncrement: false;
};
readonly otherVal: {
...;
};
}>, State.SQLite.WithDefaults<...>>, "select" | ... 6 more ... | "row">

Update rows in the table that match the where clause

Example:

db.todos.update({ status: 'completed' }).where({ id: '123' })

update
({
otherVal?: string
otherVal
: 'default' }),
// Raw SQL is also supported via { sql, bindValues }
{
sql: string
sql
: 'DELETE FROM todos',
bindValues: BindValues
bindValues
: {} },
]),
})

Reading from the database in materializers

Section titled “Reading from the database in materializers”

Sometimes it can be useful to query your current state when executing a materializer. This can be done by using ctx.query in your materializer function.

import {
const defineMaterializer: <TEventDef extends State.SQLite.EventDef.AnyWithoutFn>(_eventDef: TEventDef, materializer: State.SQLite.Materializer<TEventDef>) => State.SQLite.Materializer<TEventDef>

Type-safe wrapper for defining a single materializer.

Useful when defining materializers separately from the materializers() builder. The first argument provides type inference for the second.

@example

const todoCreatedHandler = defineMaterializer(
events.todoCreated,
({ id, text }) => tables.todos.insert({ id, text, completed: false })
)

defineMaterializer
,
import Events
Events
,
import Schema
Schema
,
import State
State
} from '@livestore/livestore'
import {
import todos
todos
} from './example.ts'
const
const events: {
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>;
}
events
= {
todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>
todoCreated
:
import Events
Events
.
synced<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>(args: {
name: "todoCreated";
schema: Schema.Codec<{
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, never, never>;
} & Omit<State.SQLite.DefineEventOptions<{
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, 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: "todoCreated"
name
: 'todoCreated',
schema: Schema.Codec<{
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, never, never>
schema
:
import Schema
Schema
.
function Struct<{
readonly id: Schema.String;
readonly text: Schema.String;
readonly completed: Schema.optional<Schema.Boolean>;
}>(fields: {
readonly id: Schema.String;
readonly text: Schema.String;
readonly completed: Schema.optional<Schema.Boolean>;
}): Schema.Struct<{
readonly id: Schema.String;
readonly text: Schema.String;
readonly completed: Schema.optional<Schema.Boolean>;
}>

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
,
completed: Schema.optional<Schema.Boolean>
completed
:
import Schema
Schema
.
const Boolean: Schema.Boolean

Type-level representation of

Boolean

.

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

When to use

Use to validate values that are already JavaScript booleans.

@since4.0.0

@seeBooleanFromBit for a schema that decodes bit literals 0 or 1 into a boolean

@since4.0.0

Boolean
.
Pipeable.pipe<Schema.Boolean, Schema.optional<Schema.Boolean>>(this: Schema.Boolean, ab: (_: Schema.Boolean) => Schema.optional<Schema.Boolean>): Schema.optional<Schema.Boolean> (+21 overloads)
pipe
(
import Schema
Schema
.
const optional: optionalLambda

Type-level representation returned by

optional

.

Marks a struct field as optional, allowing the key to be absent or undefined.

Details

The resulting property may be absent or explicitly set to undefined. Equivalent to optionalKey(UndefinedOr(S)).

Use

optionalKey

instead if you want exact optional semantics (absent only, not undefined).

Example (Defining an optional field accepting undefined)

import { Schema } from "effect"
const schema = Schema.Struct({
name: Schema.String,
age: Schema.optional(Schema.Number)
})
// { readonly name: string; readonly age?: number | undefined }
type Person = typeof schema.Type

@since3.10.0

@since3.10.0

optional
),
}),
}),
} as
type const = {
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>;
}
const
export const
const materializers: {
todoCreated: State.SQLite.Materializer<State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>>;
}
materializers
=
import State
State
.
import SQLite
SQLite
.
const materializers: <{
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>;
}>(_eventDefRecord: {
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>;
}, handlers: {
todoCreated: State.SQLite.Materializer<State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>>;
}) => {
todoCreated: State.SQLite.Materializer<State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>>;
}

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: {
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>;
}
events
, {
[
const events: {
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>;
}
events
.
todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>
todoCreated
.
name: "todoCreated"

Unique identifier for this event type. Conventionally versioned (e.g., v1.TodoCreated).

name
]:
defineMaterializer<State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>>(_eventDef: State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>, materializer: State.SQLite.Materializer<State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>>): State.SQLite.Materializer<...>

Type-safe wrapper for defining a single materializer.

Useful when defining materializers separately from the materializers() builder. The first argument provides type inference for the second.

@example

const todoCreatedHandler = defineMaterializer(
events.todoCreated,
({ id, text }) => tables.todos.insert({ id, text, completed: false })
)

defineMaterializer
(
const events: {
readonly todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>;
}
events
.
todoCreated: State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>
todoCreated
, ({
id: string
id
,
text: string
text
,
completed: boolean | undefined
completed
},
ctx: {
currentFacts: State.SQLite.EventDefFacts;
eventDef: State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>;
query: State.SQLite.MaterializerContextQuery;
event: Decoded;
}
ctx
) => {
const
const previousIds: readonly unknown[]
previousIds
=
ctx: {
currentFacts: State.SQLite.EventDefFacts;
eventDef: State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>;
query: State.SQLite.MaterializerContextQuery;
event: Decoded;
}
ctx
.
query: (args: {
query: string;
bindValues: ParamsObject;
}) => ReadonlyArray<unknown> (+1 overload)

Query with raw SQL and bind values.

query
(
import todos
todos
.
any
select
('id'))
// ctx.query also supports raw SQL via { query, bindValues }
const
const existingTodos: readonly unknown[]
existingTodos
=
ctx: {
currentFacts: State.SQLite.EventDefFacts;
eventDef: State.SQLite.EventDef<"todoCreated", {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}, {
readonly id: string;
readonly text: string;
readonly completed?: boolean | undefined;
}>;
query: State.SQLite.MaterializerContextQuery;
event: Decoded;
}
ctx
.
query: (args: {
query: string;
bindValues: ParamsObject;
}) => ReadonlyArray<unknown> (+1 overload)

Query with raw SQL and bind values.

query
({
query: string
query
: 'SELECT id FROM todos',
bindValues: ParamsObject
bindValues
: {} })
return
import todos
todos
.
any
insert
({
id: string
id
: `${
const existingTodos: readonly unknown[]
existingTodos
.
ReadonlyArray<unknown>.length: number

Gets the length of the array. This is a number one higher than the highest element defined in an array.

length
}-${
id: string
id
}`,
text: string
text
,
completed: boolean
completed
:
completed: boolean | undefined
completed
?? false,
previousIds: readonly unknown[]
previousIds
})
}),
})

A materializer is always executed in a transaction. This transaction applies to:

  • All database write operations returned by the materializer.
  • Any ctx.query calls made within the materializer, ensuring a consistent view of the data.

Materializers can return:

  • A single database write operation.
  • An array of database write operations.
  • void (i.e., no return value) if no database modifications are needed.
  • An Effect that resolves to one of the above (e.g., Effect.succeed(writeOp) or Effect.void).

The context object passed to each materializer provides query for database reads and event for the full event details.

If a materializer function throws an error, or if an Effect returned by a materializer fails, the entire transaction for that event will be rolled back. This means any database changes attempted by that materializer for the failing event will not be persisted. The error will be logged, and the system will typically halt or flag the event as problematic, depending on the specific LiveStore setup.

If the error happens on the client which tries to commit the event, the event will never be committed and pushed to the sync backend.

In the future there will be ways to configure the error-handling behaviour, e.g. to allow skipping an incoming event when a materializer fails in order to avoid the app getting stuck. However, skipping events might also lead to diverging state across clients and should be used with caution.

It’s strongly recommended to make sure your materializers are side-effect free and deterministic. This also implies passing in all necessary data via the event payload.

Example:

import {
function randomUUID(options?: RandomUUIDOptions): UUID

Generates a random RFC 4122 version 4 UUID. The UUID is generated using a cryptographic pseudorandom number generator.

@sincev15.6.0, v14.17.0

randomUUID
} from 'node:crypto'
import {
const defineMaterializer: <TEventDef extends State.SQLite.EventDef.AnyWithoutFn>(_eventDef: TEventDef, materializer: State.SQLite.Materializer<TEventDef>) => State.SQLite.Materializer<TEventDef>

Type-safe wrapper for defining a single materializer.

Useful when defining materializers separately from the materializers() builder. The first argument provides type inference for the second.

@example

const todoCreatedHandler = defineMaterializer(
events.todoCreated,
({ id, text }) => tables.todos.insert({ id, text, completed: false })
)

defineMaterializer
,
import Events
Events
,
function nanoid(size?: number): string

Generate secure URL-friendly unique ID.

By default, the ID will have 21 symbols to have a collision probability similar to UUID v4.

import { nanoid } from 'nanoid'
model.id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqL"

@paramsize Size of the ID. The default size is 21.

@returnsA random string.

nanoid
,
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 todos
todos
} from './example.ts'
declare const
const 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
export const
const nondeterministicEvents: {
readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">>;
}
nondeterministicEvents
= {
todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">>
todoCreated
:
import Events
Events
.
synced<"v1.TodoCreated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">>(args: {
name: "v1.TodoCreated";
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">, never, never>;
} & Omit<State.SQLite.DefineEventOptions<Schema.Struct.ReadonlySide<{
readonly text: 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.TodoCreated"
name
: 'v1.TodoCreated',
schema: Schema.Codec<Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">, never, never>
schema
:
import Schema
Schema
.
function Struct<{
readonly text: Schema.String;
}>(fields: {
readonly text: Schema.String;
}): Schema.Struct<{
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
({
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
}),
}),
} as
type const = {
readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">>;
}
const
export const
const nondeterministicMaterializers: {
"v1.TodoCreated": State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">>>;
}
nondeterministicMaterializers
=
import State
State
.
import SQLite
SQLite
.
const materializers: <{
readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">>;
}>(_eventDefRecord: {
readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">>;
}, 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 nondeterministicEvents: {
readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">>;
}
nondeterministicEvents
, {
[
const nondeterministicEvents: {
readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">>;
}
nondeterministicEvents
.
todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">>
todoCreated
.
name: "v1.TodoCreated"

Unique identifier for this event type. Conventionally versioned (e.g., v1.TodoCreated).

name
]:
defineMaterializer<State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">>>(_eventDef: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">>, materializer: State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<...>, Schema.Struct.ReadonlySide<...>>>): State.SQLite.Materializer<...>

Type-safe wrapper for defining a single materializer.

Useful when defining materializers separately from the materializers() builder. The first argument provides type inference for the second.

@example

const todoCreatedHandler = defineMaterializer(
events.todoCreated,
({ id, text }) => tables.todos.insert({ id, text, completed: false })
)

defineMaterializer
(
const nondeterministicEvents: {
readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">>;
}
nondeterministicEvents
.
todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">>
todoCreated
, ({
text: string
text
}) =>
import todos
todos
.
any
insert
({
id: `${string}-${string}-${string}-${string}-${string}`
id
:
function randomUUID(options?: RandomUUIDOptions): UUID

Generates a random RFC 4122 version 4 UUID. The UUID is generated using a cryptographic pseudorandom number generator.

@sincev15.6.0, v14.17.0

randomUUID
(),
text: string
text
}),
),
})
const store: Store<LiveStoreSchema.Any, {}>
store
.
Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.commit: <readonly [{
name: "v1.TodoCreated";
args: Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly text: Schema.String;
}, "Type">;
}]>(list_0: {
name: "v1.TodoCreated";
args: Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly text: Schema.String;
}, "Type">;
}) => void (+3 overloads)
commit
(
const nondeterministicEvents: {
readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Type">, Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Encoded">>;
}
nondeterministicEvents
.
todoCreated: (args: Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly text: Schema.String;
}, "Type">) => {
name: "v1.TodoCreated";
args: Schema.Struct.ReadonlySide<{
readonly text: Schema.String;
}, "Type">;
}

Callable signature - creates a partial event with decoded arguments. The returned object can be passed directly to store.commit().

todoCreated
({
text: string
text
: 'Buy groceries' }))
export const
const deterministicEvents: {
readonly 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">>;
}
deterministicEvents
= {
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
}),
}),
} as
type const = {
readonly 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">>;
}
const
export const
const deterministicMaterializers: {
"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">>>;
}
deterministicMaterializers
=
import State
State
.
import SQLite
SQLite
.
const materializers: <{
readonly 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">>;
}>(_eventDefRecord: {
readonly 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">>;
}, 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 deterministicEvents: {
readonly 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">>;
}
deterministicEvents
, {
[
const deterministicEvents: {
readonly 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">>;
}
deterministicEvents
.
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
.
name: "v1.TodoCreated"

Unique identifier for this event type. Conventionally versioned (e.g., v1.TodoCreated).

name
]:
defineMaterializer<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">>>(_eventDef: 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">>, materializer: State.SQLite.Materializer<...>): State.SQLite.Materializer<...>

Type-safe wrapper for defining a single materializer.

Useful when defining materializers separately from the materializers() builder. The first argument provides type inference for the second.

@example

const todoCreatedHandler = defineMaterializer(
events.todoCreated,
({ id, text }) => tables.todos.insert({ id, text, completed: false })
)

defineMaterializer
(
const deterministicEvents: {
readonly 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">>;
}
deterministicEvents
.
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
, ({
id: string
id
,
text: string
text
}) =>
import todos
todos
.
any
insert
({
id: string
id
,
text: string
text
}),
),
})
const store: Store<LiveStoreSchema.Any, {}>
store
.
Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.commit: <readonly [{
name: "v1.TodoCreated";
args: Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">;
}]>(list_0: {
name: "v1.TodoCreated";
args: Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">;
}) => void (+3 overloads)
commit
(
const deterministicEvents: {
readonly 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">>;
}
deterministicEvents
.
todoCreated: (args: Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">) => {
name: "v1.TodoCreated";
args: Schema.Struct.ReadonlySide<{
readonly id: Schema.String;
readonly text: Schema.String;
}, "Type">;
}

Callable signature - creates a partial event with decoded arguments. The returned object can be passed directly to store.commit().

todoCreated
({
id: string
id
:
function nanoid(size?: number): string

Generate secure URL-friendly unique ID.

By default, the ID will have 21 symbols to have a collision probability similar to UUID v4.

import { nanoid } from 'nanoid'
model.id = nanoid() //=> "Uakgb_J5m9g-0JDMbcJqL"

@paramsize Size of the ID. The default size is 21.

@returnsA random string.

nanoid
(),
text: string
text
: 'Buy groceries' }))