Skip to main content

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.

Effect Atom provides seamless integration with Effect’s service and layer system through Atom.runtime. This allows you to create atoms that have access to Effect services while maintaining reactive state management.

Creating a runtime from a layer

Use Atom.runtime to create an AtomRuntime from an Effect Layer. This runtime can then create atoms that have access to the services provided by the layer.
import { Atom } from "@effect-atom/atom-react"
import { Effect } from "effect"

class Users extends Effect.Service<Users>()("app/Users", {
  effect: Effect.gen(function* () {
    const getAll = Effect.succeed([
      { id: "1", name: "Alice" },
      { id: "2", name: "Bob" },
      { id: "3", name: "Charlie" },
    ])
    return { getAll } as const
  }),
}) {}

// Create a `AtomRuntime` from a `Layer`.
//
//         ┌─── Atom.AtomRuntime<Users>
//         ▼
const runtimeAtom = Atom.runtime(Users.Default)

// You can then use the `AtomRuntime` to make atoms that use the services from the `Layer`.
const usersAtom = runtimeAtom.atom(
  Effect.gen(function* () {
    const users = yield* Users
    return yield* users.getAll
  }),
)

Runtime atom methods

An AtomRuntime provides several methods for creating specialized atoms:

atom

Creates a regular atom that has access to the runtime’s services:
const dataAtom = runtimeAtom.atom(
  Effect.gen(function* () {
    const users = yield* Users
    return yield* users.getAll
  }),
)

fn

Creates a function atom with access to services. See the Functions guide for details:
const createUserAtom = runtimeAtom.fn(
  Effect.fnUntraced(function* (name: string) {
    const users = yield* Users
    return yield* users.create(name)
  }),
)

pull

Creates a pull atom for streams with access to services. See the Streams guide for details:
const userStreamAtom = runtimeAtom.pull(
  Effect.gen(function* () {
    const users = yield* Users
    return yield* users.getAllStream()
  }),
)

subscriptionRef

Creates an atom from a SubscriptionRef with access to services:
const refAtom = runtimeAtom.subscriptionRef(
  Effect.gen(function* () {
    const service = yield* MyService
    return service.subscriptionRef
  }),
)

subscribable

Creates an atom from a Subscribable with access to services:
const subscribableAtom = runtimeAtom.subscribable(
  Effect.gen(function* () {
    const service = yield* MyService
    return service.subscribable
  }),
)

Adding global layers

You can add global layers to all runtimes, which is useful for setting up tracers, loggers, config providers, and other infrastructure:
import { Atom } from "@effect-atom/atom-react"
import { ConfigProvider, Layer } from "effect"

Atom.runtime.addGlobalLayer(
  Layer.setConfigProvider(ConfigProvider.fromJson(import.meta.env)),
)
Global layers are automatically provided to all AtomRuntime instances created with Atom.runtime.

Working with multiple services

You can compose multiple layers together to create a runtime with multiple services:
import { Atom } from "@effect-atom/atom-react"
import { Effect, Layer } from "effect"

class Users extends Effect.Service<Users>()("app/Users", {
  effect: Effect.gen(function* () {
    const findById = (id: string) => Effect.succeed({ id, name: "John Doe" })
    return { findById } as const
  }),
}) {}

class Posts extends Effect.Service<Posts>()("app/Posts", {
  effect: Effect.gen(function* () {
    const findByUserId = (userId: string) =>
      Effect.succeed([{ id: "1", title: "Post 1", userId }])
    return { findByUserId } as const
  }),
}) {}

// Compose layers together
const AppLayer = Layer.mergeAll(Users.Default, Posts.Default)

const runtimeAtom = Atom.runtime(AppLayer)

const userPostsAtom = runtimeAtom.atom(
  Effect.gen(function* (get: Atom.Context) {
    const users = yield* Users
    const posts = yield* Posts
    
    const user = yield* users.findById("1")
    const userPosts = yield* posts.findByUserId(user.id)
    
    return { user, posts: userPosts }
  }),
)

Runtime factory

The Atom.runtime function is actually a factory that manages layer memoization. This ensures that services are only created once and shared across atoms:
interface RuntimeFactory {
  <R, E>(
    create:
      | Layer.Layer<R, E, AtomRegistry | Reactivity.Reactivity>
      | ((get: Context) => Layer.Layer<R, E, AtomRegistry | Reactivity.Reactivity>)
  ): AtomRuntime<R, E>
  readonly memoMap: Layer.MemoMap
  readonly addGlobalLayer: <A, E>(layer: Layer.Layer<A, E, AtomRegistry | Reactivity.Reactivity>) => void
  readonly withReactivity: (
    keys: ReadonlyArray<unknown> | ReadonlyRecord<string, ReadonlyArray<unknown>>
  ) => <A extends Atom<any>>(atom: A) => A
}
You can create custom runtime factories with different memoization strategies using Atom.context({ memoMap }).

Using services in React components

When using atoms with services in React, the runtime is automatically managed:
import { Atom, Result, useAtomValue } from "@effect-atom/atom-react"
import { Effect } from "effect"

class Users extends Effect.Service<Users>()("app/Users", {
  effect: Effect.gen(function* () {
    const getAll = Effect.succeed([
      { id: "1", name: "Alice" },
      { id: "2", name: "Bob" },
    ])
    return { getAll } as const
  }),
}) {}

const runtimeAtom = Atom.runtime(Users.Default)

const usersAtom = runtimeAtom.atom(
  Effect.gen(function* () {
    const users = yield* Users
    return yield* users.getAll
  }),
)

function UsersList() {
  const result = useAtomValue(usersAtom)
  
  return Result.builder(result)
    .onInitial(() => <div>Loading...</div>)
    .onFailure((cause) => <div>Error: {Cause.pretty(cause)}</div>)
    .onSuccess((users) => (
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    ))
    .render()
}

Error handling

Services can fail, and these failures are captured in the Result type:
import { Atom } from "@effect-atom/atom-react"
import { Effect } from "effect"

class UserNotFound {
  readonly _tag = "UserNotFound"
  constructor(readonly id: string) {}
}

class Users extends Effect.Service<Users>()("app/Users", {
  effect: Effect.gen(function* () {
    const findById = (id: string) =>
      Effect.fail(new UserNotFound(id))
    return { findById } as const
  }),
}) {}

const runtimeAtom = Atom.runtime(Users.Default)

// The error type is included in the Result
//        ┌─── Atom.Atom<Result.Result<User, UserNotFound>>
//        ▼
const userAtom = runtimeAtom.atom(
  Effect.gen(function* () {
    const users = yield* Users
    return yield* users.findById("1")
  }),
)

Build docs developers (and LLMs) love