Skip to content

Effect

LiveStore itself is built on top of Effect which is a powerful library to write production-grade TypeScript code. It’s also possible (and recommended) to use Effect directly in your application code.

LiveStore uses the Effect Schema library to define schemas for the following:

  • Read model table column definitions
  • Event event payloads definitions
  • Query response types

For convenience, LiveStore re-exports the Schema module from the effect package, which is the same as if you’d import it via import { Schema } from 'effect' directly.

LiveStore’s reactive primitives (LiveQueryDef and SignalDef) implement Effect’s Equal and Hash traits, enabling efficient integration with Effect’s data structures and collections.

LiveStore integrates seamlessly with Effect Atom for reactive state management in React applications. This provides a powerful combination of Effect’s functional programming capabilities with LiveStore’s event sourcing and CQRS patterns.

Effect Atom is an external package developed by Tim Smart that provides a more Effect-idiomatic alternative to the @livestore/react package. While @livestore/react offers a straightforward React integration, Effect Atom leverages Effect API/patterns throughout, making it a natural choice for applications already using Effect.

Terminal window
pnpm install @effect-atom/atom-livestore @effect-atom/atom-react

Create a LiveStore-backed atom store with persistence and worker support using the AtomLivestore.Tag pattern:

atoms.ts
/// <reference types="vite/client" />
import {
import AtomLivestore
AtomLivestore
} from '@effect-atom/atom-livestore'
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.

@example

import { makePersistedAdapter } from '@livestore/adapter-web'
import LiveStoreWorker from './livestore.worker.ts?worker'
import LiveStoreSharedWorker from '@livestore/adapter-web/shared-worker?sharedworker'
const adapter = makePersistedAdapter({
worker: LiveStoreWorker,
sharedWorker: LiveStoreSharedWorker,
storage: { type: 'opfs' },
})

makePersistedAdapter
} from '@livestore/adapter-web'
import
const LiveStoreSharedWorker: new (options?: {
name?: string;
}) => SharedWorker
LiveStoreSharedWorker
from '@livestore/adapter-web/shared-worker?sharedworker'
import
const LiveStoreWorker: new (options?: {
name?: string;
}) => Worker
LiveStoreWorker
from '@livestore/adapter-web/worker?worker'
import {
function unstable_batchedUpdates<A, R>(callback: (a: A) => R, a: A): R (+1 overload)
unstable_batchedUpdates
} from 'react-dom'
import {
const schema: FromInputSchema.DeriveSchema<{
events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
... 5 more ...;
itemUpdated: EventDef<...>;
};
state: InternalState;
}>
schema
} from './schema.ts'
export {
const schema: FromInputSchema.DeriveSchema<{
events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
... 5 more ...;
itemUpdated: EventDef<...>;
};
state: InternalState;
}>
export schema
schema
} from './schema.ts'
// Create a persistent adapter with OPFS storage
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.

@example

import { makePersistedAdapter } from '@livestore/adapter-web'
import LiveStoreWorker from './livestore.worker.ts?worker'
import LiveStoreSharedWorker from '@livestore/adapter-web/shared-worker?sharedworker'
const adapter = makePersistedAdapter({
worker: LiveStoreWorker,
sharedWorker: LiveStoreSharedWorker,
storage: { type: 'opfs' },
})

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
,
})
// Define the store as a service tag
export class
class StoreTag
StoreTag
extends
import AtomLivestore
AtomLivestore
.
const Tag: <StoreTag>() => <Id, S, Context>(id: Id, options: CreateStoreOptions<S, Context> & {
readonly otelOptions?: Partial<OtelOptions> | undefined;
}) => AtomLivestore.AtomLiveStore<StoreTag, Id, S, Context>

@since1.0.0

Tag
<
class StoreTag
StoreTag
>()('StoreTag', {
CreateStoreOptions<FromInputSchema.DeriveSchema<{ events: { userCreated: EventDef<"userCreated", { readonly id: string; readonly name: string; readonly email: string; }, { readonly id: string; readonly name: string; readonly email: string; }, false>; ... 6 more ...; itemUpdated: EventDef<...>; }; state: InternalState; }>, {}>.schema: FromInputSchema.DeriveSchema<{
events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
... 5 more ...;
itemUpdated: EventDef<...>;
};
state: InternalState;
}>
schema
,
CreateStoreOptions<TSchema extends LiveStoreSchema, TContext = {}>.storeId: string
storeId
: 'default',
CreateStoreOptions<TSchema extends LiveStoreSchema, TContext = {}>.adapter: Adapter
adapter
,
CreateStoreOptions<FromInputSchema.DeriveSchema<{ events: { userCreated: EventDef<"userCreated", { readonly id: string; readonly name: string; readonly email: string; }, { readonly id: string; readonly name: string; readonly email: string; }, false>; ... 6 more ...; itemUpdated: EventDef<...>; }; state: InternalState; }>, {}>.batchUpdates?: (run: () => void) => void
batchUpdates
:
function unstable_batchedUpdates<A, R>(callback: (a: A) => R, a: A): R (+1 overload)
unstable_batchedUpdates
, // React batching for performance
}) {}

The StoreTag class provides the following static methods:

  • StoreTag.runtime - Access to Effect runtime
  • StoreTag.commit - Commit events to the store
  • StoreTag.store - Access store with Effect
  • StoreTag.storeUnsafe - Direct store access when store is already loaded (synchronous)
  • StoreTag.makeQuery - Create query atoms with Effect
  • StoreTag.makeQueryUnsafe - Create query atoms without Effect

Create reactive query atoms that automatically update when the underlying data changes:

queries.ts
import {
import Atom
Atom
} from '@effect-atom/atom'
import {
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.

@example

const todos$ = queryDb(tables.todos.where({ complete: true }))

@example

// Group-by raw SQL query
const colorCounts$ = queryDb({
query: sql`SELECT color, COUNT(*) as count FROM todos WHERE complete = ? GROUP BY color`,
schema: Schema.Array(Schema.Struct({
color: Schema.String,
count: Schema.Number,
})),
bindValues: [1],
})

@example

// Using contextual data when constructing the query
const makeFilteredQuery = (filter: string) =>
queryDb(tables.todos.where({ title: { op: 'like', value: filter } }), { deps: [filter] })
const filteredTodos$ = makeFilteredQuery('buy coffee')

queryDb
,
const sql: (template: TemplateStringsArray, ...args: unknown[]) => string

This is a tag function for tagged literals. it lets us get syntax highlighting on SQL queries in VSCode, but doesn't do anything at runtime. Code copied from: https://esdiscuss.org/topic/string-identity-template-tag

sql
} from '@livestore/livestore'
import {
import Schema
Schema
} from 'effect'
import {
class StoreTag
StoreTag
} from './atoms.ts'
// User schema for type safety
const
const User: Schema.Struct<{
id: typeof Schema.String;
name: typeof Schema.String;
isActive: typeof Schema.Boolean;
}>
User
=
import Schema
Schema
.
function Struct<{
id: typeof Schema.String;
name: typeof Schema.String;
isActive: typeof Schema.Boolean;
}>(fields: {
id: typeof Schema.String;
name: typeof Schema.String;
isActive: typeof Schema.Boolean;
}): Schema.Struct<{
id: typeof Schema.String;
name: typeof Schema.String;
isActive: typeof Schema.Boolean;
}> (+1 overload)

@since3.10.0

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

@since3.10.0

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

@since3.10.0

String
,
isActive: typeof Schema.Boolean
isActive
:
import Schema
Schema
.
class Boolean
export Boolean

@since3.10.0

Boolean
,
})
const
const Product: Schema.Struct<{
id: typeof Schema.String;
name: typeof Schema.String;
createdAt: typeof Schema.DateTimeUtc;
}>
Product
=
import Schema
Schema
.
function Struct<{
id: typeof Schema.String;
name: typeof Schema.String;
createdAt: typeof Schema.DateTimeUtc;
}>(fields: {
id: typeof Schema.String;
name: typeof Schema.String;
createdAt: typeof Schema.DateTimeUtc;
}): Schema.Struct<{
id: typeof Schema.String;
name: typeof Schema.String;
createdAt: typeof Schema.DateTimeUtc;
}> (+1 overload)

@since3.10.0

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

@since3.10.0

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

@since3.10.0

String
,
createdAt: typeof Schema.DateTimeUtc
createdAt
:
import Schema
Schema
.
class DateTimeUtc

Defines a schema that attempts to convert a string to a DateTime.Utc instance using the DateTime.unsafeMake constructor.

@since3.10.0

DateTimeUtc
,
})
// Search term atom for dynamic queries
export const
const searchTermAtom: Atom.Writable<string, string>
searchTermAtom
=
import Atom
Atom
.
const make: <string>(initialValue: string) => Atom.Writable<string, string> (+5 overloads)
make
<string>('')
// Re-export from utils for convenience
export {
const usersQueryAtom: Atom.Atom<Result<readonly {
readonly id: string;
readonly name: string;
readonly email: string;
readonly isActive: boolean;
readonly createdAt: Date;
}[], never>>
usersQueryAtom
as
const usersAtom: Atom.Atom<Result<readonly {
readonly id: string;
readonly name: string;
readonly email: string;
readonly isActive: boolean;
readonly createdAt: Date;
}[], never>>
export usersAtom
usersAtom
} from './utils.ts'
// Query with SQL
export const
const activeUsersAtom: Atom.Atom<Result<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], never>>
activeUsersAtom
=
class StoreTag
StoreTag
.
AtomLiveStore<StoreTag, "StoreTag", FromInputSchema.DeriveSchema<{ events: { userCreated: EventDef<"userCreated", { readonly id: string; readonly name: string; readonly email: string; }, { readonly id: string; readonly name: string; readonly email: string; }, false>; ... 6 more ...; itemUpdated: EventDef<...>; }; state: InternalState; }>, {}>.makeQuery: <readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[]>(query: LiveQueryDef<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], "def">) => Atom.Atom<Result<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], never>>

Creates a Atom that allows you to resolve a LiveQueryDef. It embeds the loading of the Store and will emit a Result that contains the result of the query

makeQuery
(
queryDb<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[]>(queryInput: QueryInputRaw<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], readonly any[]> | QueryBuilder<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], any, any>, options?: {
map?: (rows: readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[]) => readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[];
label?: string;
deps?: DepKey;
} | undefined): LiveQueryDef<...> (+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.

@example

const todos$ = queryDb(tables.todos.where({ complete: true }))

@example

// Group-by raw SQL query
const colorCounts$ = queryDb({
query: sql`SELECT color, COUNT(*) as count FROM todos WHERE complete = ? GROUP BY color`,
schema: Schema.Array(Schema.Struct({
color: Schema.String,
count: Schema.Number,
})),
bindValues: [1],
})

@example

// Using contextual data when constructing the query
const makeFilteredQuery = (filter: string) =>
queryDb(tables.todos.where({ title: { op: 'like', value: filter } }), { deps: [filter] })
const filteredTodos$ = makeFilteredQuery('buy coffee')

queryDb
({
query: string
query
:
const sql: (template: TemplateStringsArray, ...args: unknown[]) => string

This is a tag function for tagged literals. it lets us get syntax highlighting on SQL queries in VSCode, but doesn't do anything at runtime. Code copied from: https://esdiscuss.org/topic/string-identity-template-tag

sql
`SELECT * FROM users WHERE isActive = true ORDER BY name`,
schema: Schema.Schema<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], readonly any[], never>
schema
:
import Schema
Schema
.
Array<Schema.Struct<{
id: typeof Schema.String;
name: typeof Schema.String;
isActive: typeof Schema.Boolean;
}>>(value: Schema.Struct<{
id: typeof Schema.String;
name: typeof Schema.String;
isActive: typeof Schema.Boolean;
}>): Schema.Array$<Schema.Struct<{
id: typeof Schema.String;
name: typeof Schema.String;
isActive: typeof Schema.Boolean;
}>>
export Array

@since3.10.0

Array
(
const User: Schema.Struct<{
id: typeof Schema.String;
name: typeof Schema.String;
isActive: typeof Schema.Boolean;
}>
User
),
}),
)
// Static query example - dynamic queries would need a different approach
// For dynamic queries, you'd typically use a derived atom that depends on searchTermAtom
export const
const searchResultsAtom: Atom.Atom<Result<readonly {
readonly id: string;
readonly name: string;
readonly createdAt: Utc;
}[], never>>
searchResultsAtom
=
class StoreTag
StoreTag
.
AtomLiveStore<StoreTag, "StoreTag", FromInputSchema.DeriveSchema<{ events: { userCreated: EventDef<"userCreated", { readonly id: string; readonly name: string; readonly email: string; }, { readonly id: string; readonly name: string; readonly email: string; }, false>; ... 6 more ...; itemUpdated: EventDef<...>; }; state: InternalState; }>, {}>.makeQuery: <readonly {
readonly id: string;
readonly name: string;
readonly createdAt: Utc;
}[]>(query: LiveQueryDef<readonly {
readonly id: string;
readonly name: string;
readonly createdAt: Utc;
}[], "def">) => Atom.Atom<Result<readonly {
readonly id: string;
readonly name: string;
readonly createdAt: Utc;
}[], never>>

Creates a Atom that allows you to resolve a LiveQueryDef. It embeds the loading of the Store and will emit a Result that contains the result of the query

makeQuery
(
queryDb<readonly {
readonly id: string;
readonly name: string;
readonly createdAt: Utc;
}[], readonly {
readonly id: string;
readonly name: string;
readonly createdAt: Utc;
}[]>(queryInput: QueryInputRaw<readonly {
readonly id: string;
readonly name: string;
readonly createdAt: Utc;
}[], readonly any[]> | QueryBuilder<readonly {
readonly id: string;
readonly name: string;
readonly createdAt: Utc;
}[], any, any>, options?: {
map?: (rows: readonly {
readonly id: string;
readonly name: string;
readonly createdAt: Utc;
}[]) => readonly {
readonly id: string;
readonly name: string;
readonly createdAt: Utc;
}[];
label?: string;
deps?: DepKey;
} | undefined): LiveQueryDef<...> (+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.

@example

const todos$ = queryDb(tables.todos.where({ complete: true }))

@example

// Group-by raw SQL query
const colorCounts$ = queryDb({
query: sql`SELECT color, COUNT(*) as count FROM todos WHERE complete = ? GROUP BY color`,
schema: Schema.Array(Schema.Struct({
color: Schema.String,
count: Schema.Number,
})),
bindValues: [1],
})

@example

// Using contextual data when constructing the query
const makeFilteredQuery = (filter: string) =>
queryDb(tables.todos.where({ title: { op: 'like', value: filter } }), { deps: [filter] })
const filteredTodos$ = makeFilteredQuery('buy coffee')

queryDb
({
query: string
query
:
const sql: (template: TemplateStringsArray, ...args: unknown[]) => string

This is a tag function for tagged literals. it lets us get syntax highlighting on SQL queries in VSCode, but doesn't do anything at runtime. Code copied from: https://esdiscuss.org/topic/string-identity-template-tag

sql
`SELECT * FROM products ORDER BY createdAt DESC`,
schema: Schema.Schema<readonly {
readonly id: string;
readonly name: string;
readonly createdAt: Utc;
}[], readonly any[], never>
schema
:
import Schema
Schema
.
Array<Schema.Struct<{
id: typeof Schema.String;
name: typeof Schema.String;
createdAt: typeof Schema.DateTimeUtc;
}>>(value: Schema.Struct<{
id: typeof Schema.String;
name: typeof Schema.String;
createdAt: typeof Schema.DateTimeUtc;
}>): Schema.Array$<Schema.Struct<{
id: typeof Schema.String;
name: typeof Schema.String;
createdAt: typeof Schema.DateTimeUtc;
}>>
export Array

@since3.10.0

Array
(
const Product: Schema.Struct<{
id: typeof Schema.String;
name: typeof Schema.String;
createdAt: typeof Schema.DateTimeUtc;
}>
Product
),
}),
)

Access query results in React components with the useAtomValue hook. When using StoreTag.makeQuery (non-unsafe API), the result is wrapped in a Result type for proper loading and error handling:

UserList.tsx
import {
import Result
Result
,
const useAtomValue: {
<A>(atom: Atom<A>): A;
<A, B>(atom: Atom<A>, f: (_: A) => B): B;
}

@since1.0.0

useAtomValue
} from '@effect-atom/atom-react'
import {
const activeUsersAtom: Atom<Result.Result<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], never>>
activeUsersAtom
} from './queries.ts'
export function
function UserList(): JSX.Element
UserList
() {
const
const users: Result.Result<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], never>
users
=
useAtomValue<Result.Result<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], never>>(atom: Atom<Result.Result<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], never>>): Result.Result<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], never> (+1 overload)

@since1.0.0

useAtomValue
(
const activeUsersAtom: Atom<Result.Result<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], never>>
activeUsersAtom
)
return
import Result
Result
.
const builder: <Result.Result<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], never>>(self: Result.Result<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], never>) => Result.Builder<never, readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], never, true>

@since1.0.0

builder
(
const users: Result.Result<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], never>
users
)
.
onInitial<JSX.Element>(f: (result: Result.Initial<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], never>) => JSX.Element): Result.Builder<JSX.Element, readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], never, never>
onInitial
(() => <
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>Loading users...</
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>)
.
onSuccess<JSX.Element>(f: (value: readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], result: Result.Success<readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[], never>) => JSX.Element): Result.Builder<JSX.Element, never, never, never>
onSuccess
((
users: readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[]
users
) => (
<
React.JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>
ul
>
{
users: readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[]
users
.
ReadonlyArray<{ readonly id: string; readonly name: string; readonly isActive: boolean; }>.map<JSX.Element>(callbackfn: (value: {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}, index: number, array: readonly {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}[]) => JSX.Element, thisArg?: any): JSX.Element[]

Calls a defined callback function on each element of an array, and returns an array that contains the results.

@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.

@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.

map
((
user: {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}
user
) => (
<
React.JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>
li
React.Attributes.key?: React.Key | null | undefined
key
={
user: {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}
user
.
id: string
id
}>{
user: {
readonly id: string;
readonly name: string;
readonly isActive: boolean;
}
user
.
name: string
name
}</
React.JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>
li
>
))}
</
React.JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>
ul
>
))
.
onDefect<JSX.Element>(f: (defect: unknown, result: Result.Failure<never, never>) => JSX.Element): Result.Builder<JSX.Element, never, never, never>
onDefect
((
error: any
error
: any) => <
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>Error: {
error: any
error
.
any
message
}</
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>)
.
function render(): JSX.Element
render
()
}

Combine Effect services with LiveStore operations using the store’s runtime:

services.tsx
import {
const useAtomSet: <R, W, Mode extends "value" | "promise" | "promiseExit" = never>(atom: Writable<R, W>, options?: {
readonly mode?: ([R] extends [Result<any, any>] ? Mode : "value") | undefined;
}) => "promise" extends Mode ? ((value: W, options?: {
readonly signal?: AbortSignal | undefined;
} | undefined) => Promise<Result.Success<R>>) : "promiseExit" extends Mode ? ((value: W, options?: {
readonly signal?: AbortSignal | undefined;
} | undefined) => Promise<Exit<Result.Success<R>, Result.Failure<R>>>) : ((value: W | ((value: R) => W)) => void)

@since1.0.0

useAtomSet
} from '@effect-atom/atom-react'
import {
import Context

@since2.0.0

@since2.0.0

Context
,
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from 'effect'
import {
class StoreTag
StoreTag
} from './atoms.ts'
import {
const events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
productCreated: EventDef<...>;
... 4 more ...;
itemUpdated: EventDef<...>;
}
events
} from './schema.ts'
// Example service definition
export class
class MyService
MyService
extends
import Context

@since2.0.0

@since2.0.0

Context
.
const Tag: <"MyService">(id: "MyService") => <Self, Shape>() => Context.TagClass<Self, "MyService", Shape>

@example

import * as assert from "node:assert"
import { Context, Layer } from "effect"
class MyTag extends Context.Tag("MyTag")<
MyTag,
{ readonly myNum: number }
>() {
static Live = Layer.succeed(this, { myNum: 108 })
}

@since2.0.0

Tag
('MyService')<
class MyService
MyService
,
{
processItem: (name: string) => Effect.Effect<{
name: string;
metadata: Record<string, unknown>;
}>
processItem
: (
name: string
name
: string) =>
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
interface Effect<out A, out E = never, out R = never>

The Effect interface defines a value that describes a workflow or job, which can succeed or fail.

Details

The Effect interface represents a computation that can model a workflow involving various types of operations, such as synchronous, asynchronous, concurrent, and parallel interactions. It operates within a context of type R, and the result can either be a success with a value of type A or a failure with an error of type E. The Effect is designed to handle complex interactions with external resources, offering advanced features such as fiber-based concurrency, scheduling, interruption handling, and scalability. This makes it suitable for tasks that require fine-grained control over concurrency and error management.

To execute an Effect value, you need a Runtime, which provides the environment necessary to run and manage the computation.

@since2.0.0

@since2.0.0

Effect
<{
name: string
name
: string
metadata: Record<string, unknown>
metadata
:
type Record<K extends keyof any, T> = { [P in K]: T; }

Construct a type with a set of properties K of type T

Record
<string, unknown>
}>
}
>() {}
// Use the commit hook for event handling
export const
const useCommit: () => (value: {} | ((value: void) => {})) => void
useCommit
= () =>
useAtomSet<void, {}, never>(atom: Writable<void, {}>, options?: {
readonly mode?: "value" | undefined;
} | undefined): (value: {} | ((value: void) => {})) => void

@since1.0.0

useAtomSet
(
class StoreTag
StoreTag
.
AtomLiveStore<StoreTag, "StoreTag", FromInputSchema.DeriveSchema<{ events: { userCreated: EventDef<"userCreated", { readonly id: string; readonly name: string; readonly email: string; }, { readonly id: string; readonly name: string; readonly email: string; }, false>; ... 6 more ...; itemUpdated: EventDef<...>; }; state: InternalState; }>, {}>.commit: Writable<void, {}>

A Atom.Writable that allows you to commit an event to the Store.

commit
)
// Simple commit example
export const
const createItemAtom: AtomResultFn<string, void, never>
createItemAtom
=
class StoreTag
StoreTag
.
AtomLiveStore<StoreTag, "StoreTag", FromInputSchema.DeriveSchema<{ events: { userCreated: EventDef<"userCreated", { readonly id: string; readonly name: string; readonly email: string; }, { readonly id: string; readonly name: string; readonly email: string; }, false>; ... 6 more ...; itemUpdated: EventDef<...>; }; state: InternalState; }>, {}>.runtime: AtomRuntime<StoreTag, never>
runtime
.
AtomRuntime<StoreTag, never>.fn: <string>() => {
<E, A>(fn: (arg: string, get: FnContext) => Effect.Effect<A, E, StoreTag | Scope | AtomRegistry | Reactivity>, options?: {
readonly initialValue?: A | undefined;
readonly reactivityKeys?: ReadonlyArray<unknown> | ReadonlyRecord<string, ReadonlyArray<unknown>> | undefined;
} | undefined): AtomResultFn<string, A, E>;
<E, A>(fn: (arg: string, get: FnContext) => Stream<...>, options?: {
...;
} | undefined): AtomResultFn<...>;
} (+2 overloads)
fn
<string>()((
itemName: string
itemName
,
get: FnContext
get
) => {
return
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const sync: <void>(thunk: LazyArg<void>) => Effect.Effect<void, never, never>

Creates an Effect that represents a synchronous side-effectful computation.

Details

The provided function (thunk) must not throw errors; if it does, the error will be treated as a "defect".

This defect is not a standard error but indicates a flaw in the logic that was expected to be error-free. You can think of it similar to an unexpected crash in the program, which can be further managed or logged using tools like

catchAllDefect

.

When to Use

Use this function when you are sure the operation will not fail.

Example (Logging a Message)

import { Effect } from "effect"
const log = (message: string) =>
Effect.sync(() => {
console.log(message) // side effect
})
// ┌─── Effect<void, never, never>
// ▼
const program = log("Hello, World!")

@seetry_try for a version that can handle failures.

@since2.0.0

sync
(() => {
const
const store: Store<FromInputSchema.DeriveSchema<{
events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
... 5 more ...;
itemUpdated: EventDef<...>;
};
state: InternalState;
}>, {}> | undefined
store
=
get: FnContext
<Store<FromInputSchema.DeriveSchema<{
events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
... 5 more ...;
itemUpdated: EventDef<...>;
};
state: InternalState;
}>, {}> | undefined>(atom: Atom<...>) => Store<...> | undefined
get
(
class StoreTag
StoreTag
.
AtomLiveStore<StoreTag, "StoreTag", FromInputSchema.DeriveSchema<{ events: { userCreated: EventDef<"userCreated", { readonly id: string; readonly name: string; readonly email: string; }, { readonly id: string; readonly name: string; readonly email: string; }, false>; ... 6 more ...; itemUpdated: EventDef<...>; }; state: InternalState; }>, {}>.storeUnsafe: Atom<Store<FromInputSchema.DeriveSchema<{
events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
... 5 more ...;
itemUpdated: EventDef<...>;
};
state: InternalState;
}>, {}> | undefined>

A Atom that allows you to access the Store. It will emit the Store or undefined if has not been created yet.

storeUnsafe
)
if (
const store: Store<FromInputSchema.DeriveSchema<{
events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
... 5 more ...;
itemUpdated: EventDef<...>;
};
state: InternalState;
}>, {}> | undefined
store
) {
const store: Store<FromInputSchema.DeriveSchema<{
events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
... 5 more ...;
itemUpdated: EventDef<...>;
};
state: InternalState;
}>, {}>
store
.
Store<FromInputSchema.DeriveSchema<{ events: { userCreated: EventDef<"userCreated", { readonly id: string; readonly name: string; readonly email: string; }, { readonly id: string; readonly name: string; readonly email: string; }, false>; ... 6 more ...; itemUpdated: EventDef<...>; }; state: InternalState; }>, {}>.commit: <readonly [{
name: "itemCreated";
args: {
readonly id: string;
readonly name: string;
readonly metadata: {
readonly [x: string]: unknown;
};
};
}]>(list_0: {
name: "itemCreated";
args: {
readonly id: string;
readonly name: string;
readonly metadata: {
readonly [x: string]: unknown;
};
};
}) => void (+3 overloads)
commit
(
const events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
productCreated: EventDef<...>;
... 4 more ...;
itemUpdated: EventDef<...>;
}
events
.
itemCreated: (args: {
readonly id: string;
readonly name: string;
readonly metadata: {
readonly [x: string]: unknown;
};
}) => {
name: "itemCreated";
args: {
readonly id: string;
readonly name: string;
readonly metadata: {
readonly [x: string]: unknown;
};
};
}

Helper function to construct a partial event

itemCreated
({
id: string
id
:
var crypto: Crypto
crypto
.
Crypto.randomUUID(): `${string}-${string}-${string}-${string}-${string}` (+1 overload)
randomUUID
(),
name: string
name
:
itemName: string
itemName
,
metadata: {
readonly [x: string]: unknown;
}
metadata
: {
createdAt: string
createdAt
: new
var Date: DateConstructor
new () => Date (+3 overloads)
Date
().
Date.toISOString(): string

Returns a date as a string value in ISO format.

toISOString
() },
}),
)
}
})
})
// Use in a React component
export function
function CreateItemButton(): JSX.Element
CreateItemButton
() {
const
const createItem: (value: string | typeof Reset | ((value: Result<void, never>) => string | typeof Reset)) => void
createItem
=
useAtomSet<Result<void, never>, string | typeof Reset, never>(atom: Writable<Result<void, never>, string | typeof Reset>, options?: {
readonly mode?: undefined;
} | undefined): (value: string | typeof Reset | ((value: Result<void, never>) => string | typeof Reset)) => void

@since1.0.0

useAtomSet
(
const createItemAtom: AtomResultFn<string, void, never>
createItemAtom
)
const
const handleClick: () => void
handleClick
= () => {
const createItem: (value: string | typeof Reset | ((value: Result<void, never>) => string | typeof Reset)) => void
createItem
('New Item')
}
return (
<
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
React.ButtonHTMLAttributes<HTMLButtonElement>.type?: "button" | "submit" | "reset" | undefined
type
="button"
React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
onClick
={
const handleClick: () => void
handleClick
}>
Create Item
</
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>
)
}

Combine local state with LiveStore for optimistic UI updates. When using StoreTag.makeQueryUnsafe, the data is directly available:

optimistic.ts
import {
import Atom
Atom
} from '@effect-atom/atom'
import {
const pendingTodosAtom: Atom.Writable<PendingTodo[], PendingTodo[]>
pendingTodosAtom
,
const todosQueryUnsafeAtom: Atom.Atom<readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[] | undefined>
todosQueryUnsafeAtom
} from '../store-setup/utils.ts'
// Combine real and pending todos for optimistic UI
export const
const optimisticTodoAtom: Atom.Atom<(PendingTodo | {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
})[]>
optimisticTodoAtom
=
import Atom
Atom
.
const make: <(PendingTodo | {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
})[]>(create: (get: Atom.Context) => (PendingTodo | {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
})[]) => Atom.Atom<(PendingTodo | {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
})[]> (+5 overloads)
make
((
get: Atom.Context
get
) => {
const
const todos: readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[] | undefined
todos
=
get: Atom.Context
<readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[] | undefined>(atom: Atom.Atom<readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[] | undefined>) => readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[] | undefined
get
(
const todosQueryUnsafeAtom: Atom.Atom<readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[] | undefined>
todosQueryUnsafeAtom
) // Direct array, not wrapped in Result
const
const pending: PendingTodo[]
pending
=
get: Atom.Context
<PendingTodo[]>(atom: Atom.Atom<PendingTodo[]>) => PendingTodo[]
get
(
const pendingTodosAtom: Atom.Writable<PendingTodo[], PendingTodo[]>
pendingTodosAtom
)
return [...(
const todos: readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[] | undefined
todos
|| []), ...
const pending: PendingTodo[]
pending
]
})

Create computed atoms based on LiveStore queries. When using the non-unsafe API, handle the Result type:

derived.ts
import {
import Atom
Atom
} from '@effect-atom/atom'
import {
import Result
Result
} from '@effect-atom/atom-react'
import {
const todosQueryAtom: Atom.Atom<Result.Result<readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[], never>>
todosQueryAtom
} from '../store-setup/utils.ts'
// Derive statistics from todos
export const
const todoStatsAtom: Atom.Atom<Result.Result<{
total: number;
completed: number;
pending: number;
}, never>>
todoStatsAtom
=
import Atom
Atom
.
const make: <Result.Result<{
total: number;
completed: number;
pending: number;
}, never>>(create: (get: Atom.Context) => Result.Result<{
total: number;
completed: number;
pending: number;
}, never>) => Atom.Atom<Result.Result<{
total: number;
completed: number;
pending: number;
}, never>> (+5 overloads)
make
((
get: Atom.Context
get
) => {
const
const todos: Result.Result<readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[], never>
todos
=
get: Atom.Context
<Result.Result<readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[], never>>(atom: Atom.Atom<Result.Result<readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[], never>>) => Result.Result<readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[], never>
get
(
const todosQueryAtom: Atom.Atom<Result.Result<readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[], never>>
todosQueryAtom
) // Result wrapped
return
import Result
Result
.
const map: <never, readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[], {
total: number;
completed: number;
pending: number;
}>(self: Result.Result<readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[], never>, f: (a: readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[]) => {
total: number;
completed: number;
pending: number;
}) => Result.Result<{
total: number;
completed: number;
pending: number;
}, never> (+1 overload)
map
(
const todos: Result.Result<readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[], never>
todos
, (
todoList: readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[]
todoList
) => ({
total: number
total
:
todoList: readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[]
todoList
.
ReadonlyArray<T>.length: number

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

length
,
completed: number
completed
:
todoList: readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[]
todoList
.
ReadonlyArray<{ readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date; }>.filter(predicate: (value: {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}, index: number, array: readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[]) => unknown, thisArg?: any): {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[] (+1 overload)

Returns the elements of an array that meet the condition specified in a callback function.

@parampredicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.

@paramthisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value.

filter
((
t: {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}
t
) =>
t: {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}
t
.
completed: boolean
completed
).
Array<{ readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date; }>.length: number

Gets or sets the length of the array. This is a number one higher than the highest index in the array.

length
,
pending: number
pending
:
todoList: readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[]
todoList
.
ReadonlyArray<{ readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date; }>.filter(predicate: (value: {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}, index: number, array: readonly {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[]) => unknown, thisArg?: any): {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}[] (+1 overload)

Returns the elements of an array that meet the condition specified in a callback function.

@parampredicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.

@paramthisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value.

filter
((
t: {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}
t
) => !
t: {
readonly id: string;
readonly text: string;
readonly completed: boolean;
readonly createdAt: Date;
}
t
.
completed: boolean
completed
).
Array<{ readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date; }>.length: number

Gets or sets the length of the array. This is a number one higher than the highest index in the array.

length
,
}))
})

Perform multiple commits efficiently (commits are synchronous):

batch.ts
import {
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
} from 'effect'
import {
class StoreTag
StoreTag
} from '../store-setup/atoms.ts'
import {
const events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
productCreated: EventDef<...>;
... 4 more ...;
itemUpdated: EventDef<...>;
}
events
} from '../store-setup/schema.ts'
// Bulk update atom for batch operations
export const
const bulkUpdateAtom: AtomResultFn<string[], void, never>
bulkUpdateAtom
=
class StoreTag
StoreTag
.
AtomLiveStore<StoreTag, "StoreTag", FromInputSchema.DeriveSchema<{ events: { userCreated: EventDef<"userCreated", { readonly id: string; readonly name: string; readonly email: string; }, { readonly id: string; readonly name: string; readonly email: string; }, false>; ... 6 more ...; itemUpdated: EventDef<...>; }; state: InternalState; }>, {}>.runtime: AtomRuntime<StoreTag, never>
runtime
.
AtomRuntime<StoreTag, never>.fn: <string[]>() => {
<E, A>(fn: (arg: string[], get: FnContext) => Effect.Effect<A, E, StoreTag | Scope | AtomRegistry | Reactivity>, options?: {
readonly initialValue?: A | undefined;
readonly reactivityKeys?: ReadonlyArray<unknown> | ReadonlyRecord<string, ReadonlyArray<unknown>> | undefined;
} | undefined): AtomResultFn<string[], A, E>;
<E, A>(fn: (arg: string[], get: FnContext) => Stream<...>, options?: {
...;
} | undefined): AtomResultFn<...>;
} (+2 overloads)
fn
<string[]>()(
import Effect

@since2.0.0

@since2.0.0

@since2.0.0

Effect
.
const fn: <never, void, [ids: string[], get: FnContext]>(body: (ids: string[], get: FnContext) => Generator<never, void, never>) => (ids: string[], get: FnContext) => Effect.Effect<void, never, never> (+20 overloads)
fn
(function* (
ids: string[]
ids
,
get: FnContext
get
) {
const
const store: Store<FromInputSchema.DeriveSchema<{
events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
... 5 more ...;
itemUpdated: EventDef<...>;
};
state: InternalState;
}>, {}> | undefined
store
=
get: FnContext
<Store<FromInputSchema.DeriveSchema<{
events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
... 5 more ...;
itemUpdated: EventDef<...>;
};
state: InternalState;
}>, {}> | undefined>(atom: Atom<...>) => Store<...> | undefined
get
(
class StoreTag
StoreTag
.
AtomLiveStore<StoreTag, "StoreTag", FromInputSchema.DeriveSchema<{ events: { userCreated: EventDef<"userCreated", { readonly id: string; readonly name: string; readonly email: string; }, { readonly id: string; readonly name: string; readonly email: string; }, false>; ... 6 more ...; itemUpdated: EventDef<...>; }; state: InternalState; }>, {}>.storeUnsafe: Atom<Store<FromInputSchema.DeriveSchema<{
events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
... 5 more ...;
itemUpdated: EventDef<...>;
};
state: InternalState;
}>, {}> | undefined>

A Atom that allows you to access the Store. It will emit the Store or undefined if has not been created yet.

storeUnsafe
)
if (!
const store: Store<FromInputSchema.DeriveSchema<{
events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
... 5 more ...;
itemUpdated: EventDef<...>;
};
state: InternalState;
}>, {}> | undefined
store
) return
// Commit multiple events synchronously
for (const
const id: string
id
of
ids: string[]
ids
) {
const store: Store<FromInputSchema.DeriveSchema<{
events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
... 5 more ...;
itemUpdated: EventDef<...>;
};
state: InternalState;
}>, {}>
store
.
Store<FromInputSchema.DeriveSchema<{ events: { userCreated: EventDef<"userCreated", { readonly id: string; readonly name: string; readonly email: string; }, { readonly id: string; readonly name: string; readonly email: string; }, false>; ... 6 more ...; itemUpdated: EventDef<...>; }; state: InternalState; }>, {}>.commit: <readonly [{
name: "itemUpdated";
args: {
readonly id: string;
readonly status: string;
};
}]>(list_0: {
name: "itemUpdated";
args: {
readonly id: string;
readonly status: string;
};
}) => void (+3 overloads)
commit
(
const events: {
userCreated: EventDef<"userCreated", {
readonly id: string;
readonly name: string;
readonly email: string;
}, {
readonly id: string;
readonly name: string;
readonly email: string;
}, false>;
userUpdated: EventDef<"userUpdated", {
readonly id: string;
readonly name: Option<string>;
readonly email: Option<string>;
readonly isActive: Option<boolean>;
}, {
readonly id: string;
readonly name?: string | undefined;
readonly email?: string | undefined;
readonly isActive?: boolean | undefined;
}, false>;
productCreated: EventDef<...>;
... 4 more ...;
itemUpdated: EventDef<...>;
}
events
.
itemUpdated: (args: {
readonly id: string;
readonly status: string;
}) => {
name: "itemUpdated";
args: {
readonly id: string;
readonly status: string;
};
}

Helper function to construct a partial event

itemUpdated
({
id: string
id
,
status: string
status
: 'processed' }))
}
}),
)
  1. Use StoreTag.makeQuery for queries: This ensures proper Effect integration and error handling
  2. Leverage Effect services: Integrate business logic through Effect services for better testability
  3. Handle loading states: Use Result.builder pattern for consistent loading/error UI
  4. Batch React updates: Always provide batchUpdates for better performance
  5. Label queries: Add descriptive labels to queries for better debugging
  6. Type safety: Let TypeScript infer types from schemas rather than manual annotations

For a comprehensive example of LiveStore with Effect Atom in action, check out Cheffect - a recipe management application that demonstrates:

  • Complete Effect service integration
  • AI-powered recipe extraction using Effect services
  • Complex query patterns with search and filtering
  • Worker-based persistence with OPFS
  • Production-ready error handling and logging