Documentation Index
Fetch the complete documentation index at: https://mintlify.com/tim-smart/effect-atom/llms.txt
Use this file to discover all available pages before exploring further.
The Atom module provides the core reactive state management primitives for Effect Atom. It enables you to create reactive values that automatically track dependencies and propagate updates.
Type definitions
Atom
interface Atom<A> extends Pipeable, Inspectable {
readonly [TypeId]: TypeId
readonly keepAlive: boolean
readonly lazy: boolean
readonly read: (get: Context) => A
readonly refresh?: (f: <A>(atom: Atom<A>) => void) => void
readonly label?: readonly [name: string, stack: string]
readonly idleTTL?: number
}
A reactive value that can be read and automatically tracks dependencies.
Writable
interface Writable<R, W = R> extends Atom<R> {
readonly [WritableTypeId]: WritableTypeId
readonly write: (ctx: WriteContext<R>, value: W) => void
}
An atom that can also be written to, allowing both read and write operations.
Context
interface Context {
<A>(atom: Atom<A>): A
get<A>(this: Context, atom: Atom<A>): A
result<A, E>(this: Context, atom: Atom<Result.Result<A, E>>, options?: {
readonly suspendOnWaiting?: boolean | undefined
}): Effect.Effect<A, E>
once<A>(this: Context, atom: Atom<A>): A
mount<A>(this: Context, atom: Atom<A>): void
refresh<A>(this: Context, atom: Atom<A>): void
set<R, W>(this: Context, atom: Writable<R, W>, value: W): void
subscribe<A>(this: Context, atom: Atom<A>, f: (_: A) => void, options?: {
readonly immediate?: boolean
}): void
readonly registry: Registry.Registry
}
The context available when reading atom values, providing access to other atoms and lifecycle hooks.
Creation functions
make
Create an atom from various sources - effects, streams, functions, or static values.
const make: {
<A, E>(create: (get: Context) => Effect.Effect<A, E, Scope.Scope | AtomRegistry>, options?: {
readonly initialValue?: A
}): Atom<Result.Result<A, E>>
<A, E>(effect: Effect.Effect<A, E, Scope.Scope | AtomRegistry>, options?: {
readonly initialValue?: A
}): Atom<Result.Result<A, E>>
<A>(create: (get: Context) => A): Atom<A>
<A>(initialValue: A): Writable<A>
}
create
function | Effect | Stream | value
The source for the atom value. Can be:
- A function that returns a value
- A function that returns an Effect
- A function that returns a Stream
- An Effect directly
- A Stream directly
- A static value (creates a writable atom)
Optional initial value to display before async operations complete
Example - Static value
import { Atom } from "@effect-rx/rx"
const countAtom = Atom.make(0)
// Type: Writable<number>
Example - Derived value
const doubledAtom = Atom.make((get) => {
const count = get(countAtom)
return count * 2
})
Example - From Effect
const userAtom = Atom.make(
Effect.gen(function* () {
const response = yield* HttpClient.get("/api/user")
return yield* response.json
}),
{ initialValue: null }
)
// Type: Atom<Result.Result<User, HttpError>>
Example - From Stream
const messagesAtom = Atom.make(
Stream.fromEventSource("/api/events")
)
runtime
The default runtime factory for creating atoms with Effect services.
const runtime: RuntimeFactory
Example
const MyRuntime = Atom.runtime(
Layer.mergeAll(
HttpClient.layer,
DatabaseLayer
)
)
const dataAtom = MyRuntime.atom(
Effect.gen(function* () {
const db = yield* Database
return yield* db.query("SELECT * FROM users")
})
)
family
Create parameterized atoms that maintain separate instances for each parameter value.
const family: <Arg, T extends object>(
f: (arg: Arg) => T
) => (arg: Arg) => T
Function that creates an atom for each unique argument
Example
const userAtom = Atom.family((userId: string) =>
Atom.make(
Effect.flatMap(
HttpClient.get(`/api/users/${userId}`),
(res) => res.json
)
)
)
// Each userId gets its own atom instance
const user1 = userAtom("user-1")
const user2 = userAtom("user-2")
Create an atom that represents a function which can be called with arguments.
const fn: {
<Arg>(): <E, A>(
fn: (arg: Arg, get: FnContext) => Effect.Effect<A, E, Scope.Scope | AtomRegistry>,
options?: {
readonly initialValue?: A | undefined
readonly concurrent?: boolean | undefined
}
) => AtomResultFn<Arg, A, E>
<E, A, Arg = void>(
fn: (arg: Arg, get: FnContext) => Effect.Effect<A, E, Scope.Scope | AtomRegistry>,
options?: {
readonly initialValue?: A | undefined
readonly concurrent?: boolean | undefined
}
): AtomResultFn<Arg, A, E>
}
The function to execute, receiving the argument and context
Initial value before first execution
Whether to allow concurrent executions
Example
const searchFn = Atom.fn((query: string, get) =>
Effect.gen(function* () {
const client = yield* HttpClient
const response = yield* client.get(`/api/search?q=${query}`)
return yield* response.json
})
)
// In a component
registry.set(searchFn, "typescript")
const result = registry.get(searchFn) // Result.Result<SearchResults, HttpError>
pull
Create an atom that pulls values from a stream on demand.
const pull: <A, E>(
create: ((get: Context) => Stream.Stream<A, E, AtomRegistry>) | Stream.Stream<A, E, AtomRegistry>,
options?: {
readonly disableAccumulation?: boolean | undefined
}
) => Writable<PullResult<A, E>, void>
A stream or function that returns a stream
options.disableAccumulation
If true, only returns new items since last pull instead of accumulating
Example
const messagesPull = Atom.pull(
Stream.fromEventSource("/api/messages")
)
// Pull next batch of messages
registry.set(messagesPull, undefined)
const result = registry.get(messagesPull)
// { done: boolean, items: NonEmptyArray<Message> }
subscriptionRef
Create an atom from an Effect SubscriptionRef.
const subscriptionRef: {
<A>(ref: SubscriptionRef.SubscriptionRef<A> | ((get: Context) => SubscriptionRef.SubscriptionRef<A>)): Writable<A>
<A, E>(
effect:
| Effect.Effect<SubscriptionRef.SubscriptionRef<A>, E, Scope.Scope | AtomRegistry>
| ((get: Context) => Effect.Effect<SubscriptionRef.SubscriptionRef<A>, E, Scope.Scope | AtomRegistry>)
): Writable<Result.Result<A, E>, A>
}
Example
const stateAtom = Atom.subscriptionRef(
Effect.map(
SubscriptionRef.make(0),
(ref) => ref
)
)
subscribable
Create an atom from an Effect Subscribable.
const subscribable: {
<A, E>(
ref: Subscribable.Subscribable<A, E> | ((get: Context) => Subscribable.Subscribable<A, E>)
): Atom<A>
<A, E, E1>(
effect:
| Effect.Effect<Subscribable.Subscribable<A, E1>, E, Scope.Scope | AtomRegistry>
| ((get: Context) => Effect.Effect<Subscribable.Subscribable<A, E1>, E, Scope.Scope | AtomRegistry>)
): Atom<Result.Result<A, E | E1>>
}
map
Transform the value of an atom.
const map: {
<R extends Atom<any>, B>(
f: (_: Type<R>) => B
): (self: R) => [R] extends [Writable<infer _, infer RW>] ? Writable<B, RW> : Atom<B>
<R extends Atom<any>, B>(
self: R,
f: (_: Type<R>) => B
): [R] extends [Writable<infer _, infer RW>] ? Writable<B, RW> : Atom<B>
}
Example
const countAtom = Atom.make(5)
const doubledAtom = Atom.map(countAtom, (n) => n * 2)
// or with pipe
const doubledAtom2 = countAtom.pipe(
Atom.map((n) => n * 2)
)
Transform an atom with full access to the context.
const transform: {
<R extends Atom<any>, B>(
f: (get: Context) => B
): (self: R) => [R] extends [Writable<infer _, infer RW>] ? Writable<B, RW> : Atom<B>
<R extends Atom<any>, B>(
self: R,
f: (get: Context) => B
): [R] extends [Writable<infer _, infer RW>] ? Writable<B, RW> : Atom<B>
}
Transform function with context access
Example
const transformedAtom = Atom.transform(sourceAtom, (get) => {
const value = get(sourceAtom)
const other = get(otherAtom)
return value + other
})
mapResult
Map over the success value of a Result atom.
const mapResult: {
<R extends Atom<Result.Result<any, any>>, B>(
f: (_: Result.Result.Success<Type<R>>) => B
): (self: R) => Writable<Result.Result<B, Result.Result.Failure<Type<R>>>, RW> | Atom<...>
}
Example
const userAtom = Atom.make(fetchUser())
const userNameAtom = Atom.mapResult(userAtom, (user) => user.name)
debounce
Debounce atom updates by a specified duration.
const debounce: {
(duration: Duration.DurationInput): <A extends Atom<any>>(self: A) => WithoutSerializable<A>
<A extends Atom<any>>(self: A, duration: Duration.DurationInput): WithoutSerializable<A>
}
The debounce duration (e.g., “500 millis”, 500)
Example
const searchInput = Atom.make("")
const debouncedSearch = Atom.debounce(searchInput, "300 millis")
withFallback
Provide a fallback atom for when the primary atom is in Initial state.
const withFallback: {
<E2, A2>(
fallback: Atom<Result.Result<A2, E2>>
): <R extends Atom<Result.Result<any, any>>>(self: R) => Atom<Result.Result<...>>
}
Example
const primaryData = Atom.make(fetchPrimaryData())
const cachedData = Atom.make(fetchFromCache())
const dataWithFallback = Atom.withFallback(primaryData, cachedData)
Lifecycle management
keepAlive
Prevent an atom from being disposed when it has no subscribers.
const keepAlive: <A extends Atom<any>>(self: A) => A
Example
const globalState = Atom.keepAlive(
Atom.make(loadConfiguration())
)
autoDispose
Allow an atom to be disposed when it has no subscribers (reverses keepAlive).
const autoDispose: <A extends Atom<any>>(self: A) => A
Example
const temporaryData = Atom.autoDispose(cachedAtom)
setIdleTTL
Set a time-to-live for the atom after it becomes idle (no subscribers).
const setIdleTTL: {
(duration: Duration.DurationInput): <A extends Atom<any>>(self: A) => A
<A extends Atom<any>>(self: A, duration: Duration.DurationInput): A
}
How long to keep the atom alive after it becomes idle
Example
const cachedData = Atom.setIdleTTL(
Atom.make(fetchData()),
"5 minutes"
)
Reactivity integration
withReactivity
Refresh an atom whenever specified keys change in the Reactivity service.
const withReactivity: (
keys: ReadonlyArray<unknown> | ReadonlyRecord<string, ReadonlyArray<unknown>>
) => <A extends Atom<any>>(atom: A) => A
keys
Array<unknown> | Record<string, Array<unknown>>
The reactivity keys to watch
Example
const userAtom = Atom.withReactivity(["user", userId])(
Atom.make(fetchUser(userId))
)
Storage integration
kvs
Create an atom backed by a KeyValueStore.
const kvs: <A>(options: {
readonly runtime: AtomRuntime<KeyValueStore.KeyValueStore, any>
readonly key: string
readonly schema: Schema.Schema<A, any>
readonly defaultValue: LazyArg<A>
}) => Writable<A>
Runtime that provides KeyValueStore
Schema for encoding/decoding
Default value if key doesn’t exist
Example
const userPrefs = Atom.kvs({
runtime: MyRuntime,
key: "user-preferences",
schema: Schema.Struct({
theme: Schema.String,
locale: Schema.String
}),
defaultValue: () => ({ theme: "light", locale: "en" })
})
searchParam
Create an atom that syncs with a URL search parameter.
const searchParam: <A = never, I extends string = never>(
name: string,
options?: {
readonly schema?: Schema.Schema<A, I>
}
) => Writable<[A] extends [never] ? string : Option.Option<A>>
Optional schema for parsing/encoding the value
Example
// String parameter
const queryAtom = Atom.searchParam("q")
// Typed parameter
const pageAtom = Atom.searchParam("page", {
schema: Schema.NumberFromString
})
registry.set(pageAtom, Option.some(2))
// Updates URL to ?page=2
Focus management
windowFocusSignal
An atom that increments when the window gains focus.
const windowFocusSignal: Atom<number>
refreshOnWindowFocus
Refresh an atom whenever the window gains focus.
const refreshOnWindowFocus: <A extends Atom<any>>(self: A) => WithoutSerializable<A>
Example
const liveData = Atom.refreshOnWindowFocus(
Atom.make(fetchLiveData())
)
Optimistic updates
optimistic
Create an optimistic version of an atom that shows pending updates immediately.
const optimistic: <A>(self: Atom<A>) => Writable<A, Atom<Result.Result<A, unknown>>>
Example
const dataAtom = Atom.make(fetchData())
const optimisticData = Atom.optimistic(dataAtom)
// Show update immediately while request is pending
registry.set(optimisticData, updateFn)
optimisticFn
Create an optimistic function atom that shows intermediate updates.
const optimisticFn: {
<Arg, A, E>(
effect: (arg: Arg, ctx: FnContext) => Effect.Effect<A, E>,
options?: { readonly reactivityKeys?: ReadonlyArray<Reactivity.Key> }
): Writable<Result.Result<A, E>, Arg | Reset | Interrupt>
}
effect
(arg: Arg, ctx: FnContext) => Effect<A, E>
required
The effectful function to execute
options.reactivityKeys
ReadonlyArray<Reactivity.Key>
Keys to invalidate after successful execution
Example
const updateUserFn = Atom.optimisticFn(
(userId: number) => Effect.tryPromise(() =>
fetch(`/api/users/${userId}`, { method: 'PUT' }).then(r => r.json())
)
)
// Use with intermediate updates
const result = useAtomValue(updateUserFn)
const trigger = useAtomSet(updateUserFn)
fnSync
Create a synchronous function atom.
const fnSync: {
<Arg, A>(
f: (arg: Arg, ctx: FnContext) => A,
options?: { readonly reactivityKeys?: ReadonlyArray<Reactivity.Key> }
): Writable<A, Arg | Reset | Interrupt>
}
f
(arg: Arg, ctx: FnContext) => A
required
The synchronous function to execute
options.reactivityKeys
ReadonlyArray<Reactivity.Key>
Keys to invalidate after execution
Example
const calculateTotalFn = Atom.fnSync(
(items: Item[]) => items.reduce((sum, item) => sum + item.price, 0)
)
// Synchronous execution
const calculate = useAtomSet(calculateTotalFn)
calculate(cartItems) // Returns immediately
Batching
batch
Batch multiple atom updates to trigger subscribers only once.
const batch: (f: () => void) => void
Example
Atom.batch(() => {
registry.set(atom1, value1)
registry.set(atom2, value2)
registry.set(atom3, value3)
})
// Subscribers are notified once after all updates
Serialization
serializable
Mark an atom as serializable for hydration.
const serializable: {
<R extends Atom<any>, S extends Schema.Schema<Type<R>, any>>(options: {
readonly key: string
readonly schema: S
}): (self: R) => R & Serializable<S>
}
Schema for encoding/decoding
Example
const userAtom = Atom.serializable(
Atom.make(fetchUser()),
{
key: "current-user",
schema: UserSchema
}
)
Effect conversions
get
Get an atom’s value as an Effect.
const get: <A>(self: Atom<A>) => Effect.Effect<A, never, AtomRegistry>
set
Set a writable atom’s value as an Effect.
const set: {
<W>(value: W): <R>(self: Writable<R, W>) => Effect.Effect<void, never, AtomRegistry>
<R, W>(self: Writable<R, W>, value: W): Effect.Effect<void, never, AtomRegistry>
}
modify
Modify a writable atom and return a value.
const modify: {
<R, W, A>(
f: (_: R) => [returnValue: A, nextValue: W]
): (self: Writable<R, W>) => Effect.Effect<A, never, AtomRegistry>
}
update
Update an atom’s value using a function.
const update: {
<R, W>(f: (_: R) => W): (self: Writable<R, W>) => Effect.Effect<void, never, AtomRegistry>
<R, W>(self: Writable<R, W>, f: (_: R) => W): Effect.Effect<void, never, AtomRegistry>
}
Example
yield* Atom.update(counterAtom, (n) => n + 1)
refresh
Rebuild an atom’s value, re-running its computation or Effect.
const refresh: <A>(self: Atom<A>) => Effect.Effect<void, never, AtomRegistry>
Example
// Refresh data from API
yield* Atom.refresh(userDataAtom)
getResult
Get a Result atom’s value as an Effect, suspending until ready.
const getResult: <A, E>(
self: Atom<Result.Result<A, E>>,
options?: { readonly suspendOnWaiting?: boolean }
) => Effect.Effect<A, E, AtomRegistry>
If true, suspends when the Result is in a waiting state
Example
// Get the successful value or fail with the error
const userData = yield* Atom.getResult(userAtom)
toStream
Convert an atom to a Stream of its values.
const toStream: <A>(self: Atom<A>) => Stream.Stream<A, never, AtomRegistry>
toStreamResult
Convert a Result atom to a Stream, emitting only successful values.
const toStreamResult: <A, E>(
self: Atom<Result.Result<A, E>>
) => Stream.Stream<A, E, AtomRegistry>
Example
// Stream only successful user data
const userStream = Atom.toStreamResult(userAtom)
Utilities
isAtom
Check if a value is an Atom.
const isAtom: (u: unknown) => u is Atom<any>
isWritable
Check if an atom is writable.
const isWritable: <R, W>(atom: Atom<R>) => atom is Writable<R, W>
withLabel
Add a debug label to an atom.
const withLabel: {
(name: string): <A extends Atom<any>>(self: A) => A
<A extends Atom<any>>(self: A, name: string): A
}
Example
const userAtom = Atom.withLabel(
Atom.make(fetchUser()),
"current-user"
)
Type helpers
Type
Extract the value type from an Atom.
type Type<T extends Atom<any>> = T extends Atom<infer A> ? A : never
Success
Extract the success type from a Result Atom.
type Success<T extends Atom<any>> = T extends Atom<Result.Result<infer A, infer _>> ? A : never
Failure
Extract the error type from a Result Atom.
type Failure<T extends Atom<any>> = T extends Atom<Result.Result<infer _, infer E>> ? E : never