Custom elements
LiveStore can be used with custom elements/web components.
Example
Section titled “Example”See examples for a complete example.
import { const makePersistedAdapter: (options: WebAdapterOptions) => Adapter
Creates a web adapter with persistent storage (currently only supports OPFS).
Requires both a web worker and a shared worker.
On browsers without SharedWorker support (e.g. Android Chrome), this adapter
automatically falls back to single-tab mode. In single-tab mode:
- Each tab runs independently with its own leader worker
- Multi-tab synchronization is not available
- Devtools are not supported
makePersistedAdapter } from '@livestore/adapter-web'import const LiveStoreSharedWorker: new (options?: { name?: string;}) => SharedWorker
LiveStoreSharedWorker from '@livestore/adapter-web/shared-worker?sharedworker'import { const createStorePromise: <TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}, TSyncPayloadSchema extends Codec<Json, Json> = Codec<Json, Json, never, never>>({ signal, otelOptions, ...options }: CreateStoreOptionsPromise<TSchema, TContext, TSyncPayloadSchema>) => Promise<Store<TSchema, TContext>>
Create a new LiveStore Store
createStorePromise, const queryDb: { <TResultSchema, TResult = TResultSchema>(queryInput: QueryInputRaw<TResultSchema, ReadonlyArray<any>> | QueryBuilder<TResultSchema, any, any>, options?: { map?: (rows: TResultSchema) => TResult; label?: string; deps?: DepKey; }): LiveQueryDef<TResult>; <TResultSchema, TResult = TResultSchema>(queryInput: ((get: GetAtomResult) => QueryInputRaw<TResultSchema, ReadonlyArray<any>>) | ((get: GetAtomResult) => QueryBuilder<TResultSchema, any, any>), options?: { map?: (rows: TResultSchema) => TResult; label?: string; deps?: DepKey; }): LiveQueryDef<TResult>;}
NOTE queryDb is only supposed to read data. Don't use it to insert/update/delete data but use events instead.
When using contextual data when constructing the query, please make sure to include it in the deps option.
queryDb } from '@livestore/livestore'
import const LiveStoreWorker: new (options?: { name?: string;}) => Worker
LiveStoreWorker from './livestore/livestore.worker.ts?worker'import { import events
events, import schema
schema, import tables
tables } from './livestore/schema.ts'
const const adapter: Adapter
adapter = function makePersistedAdapter(options: WebAdapterOptions): Adapter
Creates a web adapter with persistent storage (currently only supports OPFS).
Requires both a web worker and a shared worker.
On browsers without SharedWorker support (e.g. Android Chrome), this adapter
automatically falls back to single-tab mode. In single-tab mode:
- Each tab runs independently with its own leader worker
- Multi-tab synchronization is not available
- Devtools are not supported
makePersistedAdapter({ storage: { readonly type: "opfs"; readonly directory?: string | undefined;}
Specifies where to persist data for this adapter
storage: { type: "opfs"
type: 'opfs' }, worker: ((options: { name: string;}) => globalThis.Worker) | (new (options: { name: string;}) => globalThis.Worker)
worker: const LiveStoreWorker: new (options?: { name?: string;}) => Worker
LiveStoreWorker, sharedWorker: ((options: { name: string;}) => globalThis.SharedWorker) | (new (options: { name: string;}) => globalThis.SharedWorker)
This is mostly an implementation detail and needed to be exposed into app code
due to a current Vite limitation (https://github.com/vitejs/vite/issues/8427).
In most cases this should look like:
import LiveStoreSharedWorker from '@livestore/adapter-web/shared-worker?sharedworker'
const adapter = makePersistedAdapter({ sharedWorker: LiveStoreSharedWorker, // ...})
sharedWorker: const LiveStoreSharedWorker: new (options?: { name?: string;}) => SharedWorker
LiveStoreSharedWorker,})
const const store: Store<any, {}>
store = await createStorePromise<any, {}, Codec<Json, Json, never, never>>({ signal, otelOptions, ...options }: CreateStoreOptionsPromise<any, {}, Codec<Json, Json, never, never>>): Promise<Store<any, {}>>
Create a new LiveStore Store
createStorePromise({ CreateStoreOptions<any, {}, Codec<Json, Json, never, never>>.schema: any
The LiveStore schema defining tables, events, and materializers.
schema, CreateStoreOptions<TSchema extends LiveStoreSchema, TContext = {}, TSyncPayloadSchema extends Codec<Json, Json> = Codec<Json, Json, never, never>>.adapter: Adapter
Adapter used for data storage and synchronization.
adapter, CreateStoreOptions<TSchema extends LiveStoreSchema, TContext = {}, TSyncPayloadSchema extends Codec<Json, Json> = Codec<Json, Json, never, never>>.storeId: string
Unique identifier for the Store instance, stable for its lifetime.
- Valid characters: Only alphanumeric characters, underscores (
_), and hyphens (-)
are allowed. Must match /^[a-zA-Z0-9_-]+$/.
- Globally unique: Use globally unique IDs (e.g., nanoid) to prevent collisions across stores.
- Use namespaces: Prefix to avoid collisions and for easier identification when debugging
(e.g.,
app-root, workspace-abc123, issue-456)
storeId: 'custom-elements-demo' })
const const visibleTodos$: LiveQueryDef<unknown, "def">
visibleTodos$ = queryDb<unknown, unknown>(queryInput: QueryInputRaw<unknown, readonly any[]> | QueryBuilder<unknown, any, any>, options?: { map?: (rows: unknown) => unknown; label?: string; deps?: DepKey;} | undefined): LiveQueryDef<unknown, "def"> (+1 overload)
NOTE queryDb is only supposed to read data. Don't use it to insert/update/delete data but use events instead.
When using contextual data when constructing the query, please make sure to include it in the deps option.
queryDb(import tables
tables.any
todos.any
where({ deletedAt: null
deletedAt: null }))
class class TodoListElement
TodoListElement extends var HTMLElement: { new (): HTMLElement; prototype: HTMLElement;}
The HTMLElement interface represents any HTML element. Some elements directly implement this interface, while others implement it via an interface that inherits it.
HTMLElement { private TodoListElement.list: HTMLUListElement
list: interface HTMLUListElement
The HTMLUListElement interface provides special properties (beyond those defined on the regular HTMLElement interface it also has available to it by inheritance) for manipulating unordered list () elements.
HTMLUListElement private TodoListElement.input: HTMLInputElement
input: interface HTMLInputElement
The HTMLInputElement interface provides special properties and methods for manipulating the options, layout, and presentation of elements.
HTMLInputElement
constructor() { super() const const shadow: ShadowRoot
shadow = this.Element.attachShadow(init: ShadowRootInit): ShadowRoot
The Element.attachShadow() method attaches a shadow DOM tree to the specified element and returns a reference to its ShadowRoot.
attachShadow({ ShadowRootInit.mode: ShadowRootMode
mode: 'open' })
this.TodoListElement.input: HTMLInputElement
input = var document: Document
window.document returns a reference to the document contained in the window.
document.Document.createElement<"input">(tagName: "input", options?: ElementCreationOptions): HTMLInputElement (+2 overloads)
In an HTML document, the document.createElement() method creates the HTML element specified by localName, or an HTMLUnknownElement if localName isn't recognized.
createElement('input') this.TodoListElement.input: HTMLInputElement
input.HTMLInputElement.placeholder: string
The placeholder property of the HTMLInputElement interface represents a hint to the user of what can be entered in the control. It reflects the element's placeholder attribute.
placeholder = 'What needs to be done?'
this.TodoListElement.list: HTMLUListElement
list = var document: Document
window.document returns a reference to the document contained in the window.
document.Document.createElement<"ul">(tagName: "ul", options?: ElementCreationOptions): HTMLUListElement (+2 overloads)
In an HTML document, the document.createElement() method creates the HTML element specified by localName, or an HTMLUnknownElement if localName isn't recognized.
createElement('ul') this.TodoListElement.list: HTMLUListElement
list.ElementCSSInlineStyle.style: CSSStyleDeclaration
style.CSSStyleProperties.listStyle: string
The list-style CSS shorthand property allows you to set all the list style properties at once.
listStyle = 'none' this.TodoListElement.list: HTMLUListElement
list.ElementCSSInlineStyle.style: CSSStyleDeclaration
style.CSSStyleProperties.padding: string
The padding CSS shorthand property sets the padding area on all four sides of an element at once.
padding = '0' this.TodoListElement.list: HTMLUListElement
list.ElementCSSInlineStyle.style: CSSStyleDeclaration
style.CSSStyleProperties.margin: string
The margin CSS shorthand property sets the margin area on all four sides of an element.
margin = '16px 0 0'
this.TodoListElement.input: HTMLInputElement
input.HTMLInputElement.addEventListener<"keydown">(type: "keydown", listener: (this: HTMLInputElement, ev: KeyboardEvent) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)
The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.
Adds a new handler for the type event. Any given listener is added only once per type and per capture option value.
If the once option is true, the listener is removed after the next time a type event is dispatched.
The capture option is not used by Node.js in any functional way other than tracking registered event listeners per the EventTarget specification.
Specifically, the capture option is used as part of the key when registering a listener.
Any individual listener may be added once with capture = false, and once with capture = true.
The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.
Adds a new handler for the type event. Any given listener is added only once per type and per capture option value.
If the once option is true, the listener is removed after the next time a type event is dispatched.
The capture option is not used by Node.js in any functional way other than tracking registered event listeners per the EventTarget specification.
Specifically, the capture option is used as part of the key when registering a listener.
Any individual listener may be added once with capture = false, and once with capture = true.
addEventListener('keydown', (event: KeyboardEvent
event) => { if (event: KeyboardEvent
event.KeyboardEvent.key: string
The KeyboardEvent interface's key read-only property returns the value of the key pressed by the user, taking into consideration the state of modifier keys such as Shift as well as the keyboard locale and layout.
key === 'Enter' && this.TodoListElement.input: HTMLInputElement
input.HTMLInputElement.value: string
The value property of the HTMLInputElement interface represents the current value of the element as a string.
value.String.trim(): string
Removes the leading and trailing white space and line terminator characters from a string.
trim() !== '') { const store: Store<any, {}>
store.Store<any, {}>.commit: <readonly [any]>(list_0: any) => void (+3 overloads)
commit(import events
events.any
todoCreated({ id: `${string}-${string}-${string}-${string}-${string}`
id: var crypto: Crypto
crypto.Crypto.randomUUID(): `${string}-${string}-${string}-${string}-${string}` (+2 overloads)
randomUUID(), text: string
text: this.TodoListElement.input: HTMLInputElement
input.HTMLInputElement.value: string
The value property of the HTMLInputElement interface represents the current value of the element as a string.
value.String.trim(): string
Removes the leading and trailing white space and line terminator characters from a string.
trim() })) this.TodoListElement.input: HTMLInputElement
input.HTMLInputElement.value: string
The value property of the HTMLInputElement interface represents the current value of the element as a string.
value = '' } })
const shadow: ShadowRoot
shadow.ParentNode.append(...nodes: (Node | string)[]): void
Inserts nodes after the last child of node, while replacing strings in nodes with equivalent Text nodes.
Throws a "HierarchyRequestError" DOMException if the constraints of the node tree are violated.
append(this.TodoListElement.input: HTMLInputElement
input, this.TodoListElement.list: HTMLUListElement
list) }
TodoListElement.connectedCallback(): void
connectedCallback(): void { this.TodoListElement.renderTodos(todos: ReadonlyArray<typeof tables.todos.Type>): void
renderTodos(var Array: ArrayConstructor
Array.ArrayConstructor.from<any>(iterable: Iterable<any> | ArrayLike<any>): any[] (+3 overloads)
Creates an array from an iterable object.
from(const store: Store<any, {}>
store.Store<any, {}>.query: <Iterable<any> | ArrayLike<any>>(query: Queryable<Iterable<any> | ArrayLike<any>> | { query: string; bindValues: Bindable; schema?: Decoder<Iterable<any> | ArrayLike<any>, never>;}, options?: { otelContext?: Context; debugRefreshReason?: RefreshReason;}) => Iterable<any> | ArrayLike<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(import tables
tables.any
todos.any
where({ deletedAt: null
deletedAt: null }))))
const store: Store<any, {}>
store.Store<any, {}>.subscribe: <unknown>(query: Queryable<unknown>, onUpdate: (value: unknown) => void, options?: SubscribeOptions<unknown> | undefined) => Unsubscribe (+1 overload)
subscribe(const visibleTodos$: LiveQueryDef<unknown, "def">
visibleTodos$, (todos: unknown
todos) => this.TodoListElement.renderTodos(todos: ReadonlyArray<typeof tables.todos.Type>): void
renderTodos(todos: unknown
todos)) }
private TodoListElement.renderTodos(todos: ReadonlyArray<typeof tables.todos.Type>): void
renderTodos(todos: readonly any[]
todos: interface ReadonlyArray<T>
ReadonlyArray<typeof import tables
tables.any
todos.any
Type>): void { const const nodes: HTMLLIElement[]
nodes = var Array: ArrayConstructor
Array.ArrayConstructor.from<any, HTMLLIElement>(iterable: Iterable<any> | ArrayLike<any>, mapfn: (v: any, k: number) => HTMLLIElement, thisArg?: any): HTMLLIElement[] (+3 overloads)
Creates an array from an iterable object.
from(todos: readonly any[]
todos, (todo: any
todo) => { const const item: HTMLLIElement
item = var document: Document
window.document returns a reference to the document contained in the window.
document.Document.createElement<"li">(tagName: "li", options?: ElementCreationOptions): HTMLLIElement (+2 overloads)
In an HTML document, the document.createElement() method creates the HTML element specified by localName, or an HTMLUnknownElement if localName isn't recognized.
createElement('li') const item: HTMLLIElement
item.Element.textContent: string | null
textContent = todo: any
todo.any
text const item: HTMLLIElement
item.ElementCSSInlineStyle.style: CSSStyleDeclaration
style.CSSStyleProperties.cursor: string
The cursor CSS property sets the mouse cursor, if any, to show when the mouse pointer is over an element.
cursor = 'pointer' const item: HTMLLIElement
item.HTMLLIElement.addEventListener<"click">(type: "click", listener: (this: HTMLLIElement, ev: PointerEvent) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)
The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.
Adds a new handler for the type event. Any given listener is added only once per type and per capture option value.
If the once option is true, the listener is removed after the next time a type event is dispatched.
The capture option is not used by Node.js in any functional way other than tracking registered event listeners per the EventTarget specification.
Specifically, the capture option is used as part of the key when registering a listener.
Any individual listener may be added once with capture = false, and once with capture = true.
The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.
Adds a new handler for the type event. Any given listener is added only once per type and per capture option value.
If the once option is true, the listener is removed after the next time a type event is dispatched.
The capture option is not used by Node.js in any functional way other than tracking registered event listeners per the EventTarget specification.
Specifically, the capture option is used as part of the key when registering a listener.
Any individual listener may be added once with capture = false, and once with capture = true.
addEventListener('click', () => { const store: Store<any, {}>
store.Store<any, {}>.commit: <readonly [any]>(list_0: any) => void (+3 overloads)
commit( todo: any
todo.any
completed === true ? import events
events.any
todoUncompleted({ id: any
id: todo: any
todo.any
id }) : import events
events.any
todoCompleted({ id: any
id: todo: any
todo.any
id }), ) })
const const deleteButton: HTMLButtonElement
deleteButton = var document: Document
window.document returns a reference to the document contained in the window.
document.Document.createElement<"button">(tagName: "button", options?: ElementCreationOptions): HTMLButtonElement (+2 overloads)
In an HTML document, the document.createElement() method creates the HTML element specified by localName, or an HTMLUnknownElement if localName isn't recognized.
createElement('button') const deleteButton: HTMLButtonElement
deleteButton.HTMLButtonElement.type: "button" | "reset" | "submit"
The type property of the HTMLButtonElement interface is a string that indicates the behavior type of the element.
type = 'button' const deleteButton: HTMLButtonElement
deleteButton.Element.textContent: string | null
textContent = '✕' const deleteButton: HTMLButtonElement
deleteButton.ElementCSSInlineStyle.style: CSSStyleDeclaration
style.CSSStyleProperties.marginLeft: string
The margin-left CSS property sets the margin area on the left side of an element. A positive value places it farther from its neighbors, while a negative value places it closer.
marginLeft = '8px' const deleteButton: HTMLButtonElement
deleteButton.HTMLButtonElement.addEventListener<"click">(type: "click", listener: (this: HTMLButtonElement, ev: PointerEvent) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)
The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.
Adds a new handler for the type event. Any given listener is added only once per type and per capture option value.
If the once option is true, the listener is removed after the next time a type event is dispatched.
The capture option is not used by Node.js in any functional way other than tracking registered event listeners per the EventTarget specification.
Specifically, the capture option is used as part of the key when registering a listener.
Any individual listener may be added once with capture = false, and once with capture = true.
The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.
Adds a new handler for the type event. Any given listener is added only once per type and per capture option value.
If the once option is true, the listener is removed after the next time a type event is dispatched.
The capture option is not used by Node.js in any functional way other than tracking registered event listeners per the EventTarget specification.
Specifically, the capture option is used as part of the key when registering a listener.
Any individual listener may be added once with capture = false, and once with capture = true.
addEventListener('click', (event: PointerEvent
event) => { event: PointerEvent
event.Event.stopPropagation(): void (+2 overloads)
This is not used in Node.js and is provided purely for completeness.
stopPropagation() const store: Store<any, {}>
store.Store<any, {}>.commit: <readonly [any]>(list_0: any) => void (+3 overloads)
commit(import events
events.any
todoDeleted({ id: any
id: todo: any
todo.any
id, deletedAt: Date
deletedAt: new var Date: DateConstructornew () => Date (+3 overloads)
Date() })) })
const const row: HTMLDivElement
row = var document: Document
window.document returns a reference to the document contained in the window.
document.Document.createElement<"div">(tagName: "div", options?: ElementCreationOptions): HTMLDivElement (+2 overloads)
In an HTML document, the document.createElement() method creates the HTML element specified by localName, or an HTMLUnknownElement if localName isn't recognized.
createElement('div') const row: HTMLDivElement
row.ElementCSSInlineStyle.style: CSSStyleDeclaration
style.CSSStyleProperties.display: string
The display CSS property sets whether an element is treated as a block or inline box and the layout used for its children, such as flow layout, grid or flex.
display = 'flex' const row: HTMLDivElement
row.ElementCSSInlineStyle.style: CSSStyleDeclaration
style.CSSStyleProperties.alignItems: string
The CSS align-items property sets the align-self value on all direct children as a group. In flexbox, it controls the alignment of items on the cross axis. In grid layout, it controls the alignment of items on the block axis within their grid areas.
alignItems = 'center' const row: HTMLDivElement
row.Node.appendChild<HTMLLIElement>(node: HTMLLIElement): HTMLLIElement
The appendChild() method of the Node interface adds a node to the end of the list of children of a specified parent node.
appendChild(const item: HTMLLIElement
item) const row: HTMLDivElement
row.Node.appendChild<HTMLButtonElement>(node: HTMLButtonElement): HTMLButtonElement
The appendChild() method of the Node interface adds a node to the end of the list of children of a specified parent node.
appendChild(const deleteButton: HTMLButtonElement
deleteButton)
const const wrapper: HTMLLIElement
wrapper = var document: Document
window.document returns a reference to the document contained in the window.
document.Document.createElement<"li">(tagName: "li", options?: ElementCreationOptions): HTMLLIElement (+2 overloads)
In an HTML document, the document.createElement() method creates the HTML element specified by localName, or an HTMLUnknownElement if localName isn't recognized.
createElement('li') const wrapper: HTMLLIElement
wrapper.Node.appendChild<HTMLDivElement>(node: HTMLDivElement): HTMLDivElement
The appendChild() method of the Node interface adds a node to the end of the list of children of a specified parent node.
appendChild(const row: HTMLDivElement
row) return const wrapper: HTMLLIElement
wrapper })
this.TodoListElement.list: HTMLUListElement
list.ParentNode.replaceChildren(...nodes: (Node | string)[]): void
Replace all children of node with nodes, while replacing strings in nodes with equivalent Text nodes.
Throws a "HierarchyRequestError" DOMException if the constraints of the node tree are violated.
replaceChildren(...const nodes: HTMLLIElement[]
nodes) }}
var customElements: CustomElementRegistry
The customElements read-only property of the Window interface returns a reference to the CustomElementRegistry object, which can be used to register new custom elements and get information about previously registered custom elements.
customElements.CustomElementRegistry.define(name: string, constructor: CustomElementConstructor, options?: ElementDefinitionOptions): void
The define() method of the CustomElementRegistry interface adds a definition for a custom element to the custom element registry, mapping its name to the constructor which will be used to create it.
define('todo-list', class TodoListElement
TodoListElement)import { import Events
Events, const makeSchema: <TInputSchema extends InputSchema>(inputSchema: TInputSchema) => FromInputSchema.DeriveSchema<TInputSchema>
makeSchema, import Schema
Schema, type SessionIdSymbol = typeof SessionIdSymbolconst SessionIdSymbol: typeof SessionIdSymbol
Can be used in queries to refer to the current session id.
Will be replaced with the actual session id at runtime.
In client document table:
const uiState = State.SQLite.clientDocument({ name: 'ui_state', schema: Schema.Struct({ theme: Schema.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'
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:
- Using explicit column definitions
- Using an Effect Schema (either the
name property needs to be provided or the schema needs to have a title/identifier)
// Using explicit columnsconst usersTable = State.SQLite.table({ name: 'users', columns: { id: State.SQLite.text({ primaryKey: true }), name: State.SQLite.text({ nullable: false }), email: State.SQLite.text({ nullable: false }), age: State.SQLite.integer({ nullable: true }), },})
// Using Effect Schema with annotationsimport { Schema } from '@livestore/utils/effect'
const UserSchema = Schema.Struct({ id: Schema.Int.pipe(State.SQLite.withPrimaryKey).pipe(State.SQLite.withAutoIncrement), email: Schema.String.pipe(State.SQLite.withUnique), name: Schema.String, active: Schema.Boolean.pipe(State.SQLite.withDefault(true)), createdAt: Schema.optional(Schema.Date),})
// Option 1: With explicit nameconst usersTable = State.SQLite.table({ name: 'users', schema: UserSchema,})
// Option 2: With name from schema annotation (title or identifier)const AnnotatedUserSchema = UserSchema.annotate({ title: 'users' })const usersTable2 = State.SQLite.table({ schema: AnnotatedUserSchema,})
// Adding indexesconst PostSchema = Schema.Struct({ id: Schema.String.pipe(State.SQLite.withPrimaryKey), title: Schema.String, authorId: Schema.String, createdAt: Schema.Date,}).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.
DateFromMillis }), }, }), uiState: State.SQLite.ClientDocumentTableDef<"uiState", Schema.Struct.ReadonlySide<{ readonly newTodoText: Schema.String;}, "Type">, Schema.Struct.ReadonlySide<{ readonly newTodoText: Schema.String;}, "Encoded">, { partialSet: true; default: { id: typeof SessionIdSymbol; value: { readonly newTodoText: ""; }; };}>
uiState: import State
State.import SQLite
SQLite.clientDocument<"uiState", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly newTodoText: Schema.String;}, "Type">, Schema.Struct.ReadonlySide<{ readonly newTodoText: Schema.String;}, "Encoded">, { readonly name: "uiState"; readonly schema: Schema.Struct<{ readonly newTodoText: Schema.String; }>; readonly default: { readonly id: typeof SessionIdSymbol; readonly value: { readonly newTodoText: ""; }; };}>({ name, schema: valueSchema, ...inputOptions }: { name: "uiState"; schema: Schema.Codec<Schema.Struct.ReadonlySide<{ readonly newTodoText: Schema.String; }, "Type">, Schema.Struct.ReadonlySide<...>, never, never>;} & { readonly name: "uiState"; readonly schema: Schema.Struct<{ readonly newTodoText: Schema.String; }>; readonly default: { readonly id: typeof SessionIdSymbol; readonly value: { readonly newTodoText: ""; }; };}): State.SQLite.ClientDocumentTableDef<...>export clientDocument
Special:
- Synced across client sessions (e.g. tabs) but not across different clients
- Derived setters
- Emits client-only events
- Has implicit setter-materializers
- Similar to
React.useState (except it's persisted)
Careful:
- When changing the table definitions in a non-backwards compatible way, the state might be lost without
explicit materializers to handle the old auto-generated events
Usage:
// Querying data// `'some-id'` can be ommited for SessionIdSymbolstore.queryDb(clientDocumentTable.get('some-id'))
// Setting data// Again, `'some-id'` can be ommited for SessionIdSymbolstore.commit(clientDocumentTable.set({ someField: 'some-value' }, 'some-id'))
clientDocument({ name: "uiState"
name: 'uiState', schema: Schema.Codec<Schema.Struct.ReadonlySide<{ readonly newTodoText: Schema.String;}, "Type">, Schema.Struct.ReadonlySide<{ readonly newTodoText: Schema.String;}, "Encoded">, never, never> & Schema.Struct<{ readonly newTodoText: Schema.String;}>
schema: import Schema
Schema.function Struct<{ readonly newTodoText: Schema.String;}>(fields: { readonly newTodoText: Schema.String;}): Schema.Struct<{ readonly newTodoText: 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 }
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".
String }), default: { readonly id: typeof SessionIdSymbol; readonly value: { readonly newTodoText: ""; };}
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: "";}
value: { newTodoText: ""
newTodoText: '' } }, }),}
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<...>; 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.
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 }
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".
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".
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.
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 }
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".
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.
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 }
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".
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.
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 }
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".
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.
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.
isDateValid()), }), }), uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<"uiState", Schema.Struct.ReadonlySide<{ readonly newTodoText: Schema.String;}, "Type">, { partialSet: true; default: { id: typeof SessionIdSymbol; value: { readonly newTodoText: ""; }; };}>
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;}, "Type">, Schema.Struct.ReadonlySide<{ readonly newTodoText: Schema.String;}, "Encoded">, { partialSet: true; default: { id: typeof SessionIdSymbol; value: { readonly newTodoText: ""; }; };}>
uiState.ClientDocumentTableDef<TName extends string, TType, TEncoded, TOptions extends ClientDocumentTableOptions<TType>>.Trait<"uiState", Struct<Fields extends Struct.Fields>.ReadonlySide<{ readonly newTodoText: String; }, "Type">, Struct.ReadonlySide<{ readonly newTodoText: String; }, "Encoded">, { ...; }>.set: State.SQLite.ClientDocumentTableDef.SetEventDefLike<"uiState", Schema.Struct.ReadonlySide<{ readonly newTodoText: Schema.String;}, "Type">, { partialSet: true; default: { id: typeof SessionIdSymbol; value: { readonly newTodoText: ""; }; };}>
Derived event definition for setting the value of the client document table.
If the document doesn't exist yet, the first .set event will create it.
set,}
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<...>;}
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<...>; 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<...>; 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
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<...>; 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.
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 }),})
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<...>;}
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<...>; 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<...>; 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<...>; 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<...>; uiStateSet: State.SQLite.ClientDocumentTableDef.SetEventDefLike<...>;}
events, state: InternalState
state })