Reactivity System
LiveStore has a high-performance, fine-grained reactivity system built in which is similar to Signals (e.g. in SolidJS).
Defining reactive state
Section titled “Defining reactive state”LiveStore provides 3 types of reactive state:
- Reactive SQL queries on top of SQLite state (
queryDb()
) - Reactive state values (
signal()
) - Reactive computed values (
computed()
)
Reactive state variables end on a $
by convention (e.g. todos$
). The label
option is optional but can be used to identify the reactive state variable in the devtools.
Reactive SQL queries
Section titled “Reactive SQL queries”import { queryDb } from '@livestore/livestore'
const todos$ = queryDb(tables.todos.orderBy('createdAt', 'desc'), { label: 'todos$' })
// Or using callback syntax to depend on other queriesconst todos$ = queryDb((get) => { const { showCompleted } = get(uiState$) return tables.todos.where(showCompleted ? { completed: true } : {})}, { label: 'todos$' })
Signals
Section titled “Signals”Signals are reactive state values that can be set and get. This can be useful for state that is not materialized from events into SQLite tables.
import { signal } from '@livestore/livestore'
const now$ = signal(Date.now(), { label: 'now$' })
setInterval(() => { store.setSignal(now$, Date.now())}, 1000)
// Counter exampleconst num$ = signal(0, { label: 'num$' })const increment = () => store.setSignal(num$, (prev) => prev + 1)
increment()increment()
console.log(store.query(num$)) // 2
Computed values
Section titled “Computed values”import { computed } from '@livestore/livestore'
const num$ = signal(0, { label: 'num$' })const duplicated$ = computed((get) => get(num$) * 2, { label: 'duplicated$' })
Accessing reactive state
Section titled “Accessing reactive state”Reactive state is always bound to a Store
instance. You can access the current value of reactive state the following ways:
Using the Store
instance
Section titled “Using the Store instance”// One-off queryconst count = store.query(count$)
// By subscribing to the reactive state valueconst unsub = count$.subscribe((count) => { console.log(count)})
Via framework integrations
Section titled “Via framework integrations”import { useQuery } from '@livestore/react'
const MyComponent = () => { const value = useQuery(state$)
return <div>{value}</div>}
import { query } from '@livestore/solid'
const MyComponent = () => { const value = query(state$)
return <div>{value}</div>}
Further reading
Section titled “Further reading”- Riffle: Building data-centric apps with a reactive relational database
- Adapton / miniAdapton
Related technologies
Section titled “Related technologies”- Signia: Signia is a minimal, fast, and scalable signals library for TypeScript developed by TLDraw.