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.

What is derived state?

Derived state is computed from other atoms. When the source atoms change, derived atoms automatically recalculate. This creates a reactive dependency graph where changes propagate automatically. Derived atoms are read-only by default and only recompute when their dependencies change.

Creating derived atoms

Using the get function

The most flexible way to create derived state is using a function that receives the get context:
import { Atom } from "@effect-atom/atom"

const firstNameAtom = Atom.make("Alice")
const lastNameAtom = Atom.make("Smith")

const fullNameAtom = Atom.make((get) => {
  const firstName = get(firstNameAtom)
  const lastName = get(lastNameAtom)
  return `${firstName} ${lastName}`
})
// Type: Atom<string>
When either firstNameAtom or lastNameAtom changes, fullNameAtom automatically recalculates.

Using Atom.map

For simple transformations of a single atom, use Atom.map:
import { Atom } from "@effect-atom/atom"

const countAtom = Atom.make(5)

// Map to a new value
const doubledAtom = Atom.map(countAtom, (count) => count * 2)
// Type: Atom<number>

const labeledAtom = Atom.map(countAtom, (count) => `Count: ${count}`)
// Type: Atom<string>
The map function has a pipeable API:
import { Atom } from "@effect-atom/atom"

const countAtom = Atom.make(5)

const processedAtom = countAtom.pipe(
  Atom.map(n => n * 2),
  Atom.map(n => `Value: ${n}`)
)

Combining multiple atoms

Derive state from multiple atoms using the get function:
import { Atom } from "@effect-atom/atom"

const priceAtom = Atom.make(100)
const quantityAtom = Atom.make(2)
const taxRateAtom = Atom.make(0.1)

const totalAtom = Atom.make((get) => {
  const price = get(priceAtom)
  const quantity = get(quantityAtom)
  const taxRate = get(taxRateAtom)
  
  const subtotal = price * quantity
  const tax = subtotal * taxRate
  return subtotal + tax
})
// Automatically updates when price, quantity, or taxRate changes

Deriving from Result atoms

When working with atoms that contain Result types (from effects), use special combinators:

Using mapResult

Transform the success value of a Result atom:
import { Atom, Result } from "@effect-atom/atom"
import { Effect } from "effect"

const userAtom = Atom.make(
  Effect.succeed({ id: 1, name: "Alice" })
)
// Type: Atom<Result.Result<{ id: number; name: string }, never>>

const userNameAtom = Atom.mapResult(userAtom, (user) => user.name)
// Type: Atom<Result.Result<string, never>>
This preserves the Result wrapper while transforming the success value.

Using get.result in effects

When creating effect-based derived atoms, use get.result to work with Result atoms:
import { Atom } from "@effect-atom/atom"
import { Effect } from "effect"

const userAtom = Atom.make(
  Effect.succeed({ id: 1, name: "Alice" })
)

const greetingAtom = Atom.make((get) =>
  Effect.gen(function* () {
    // get.result unwraps the Result into an Effect
    const user = yield* get.result(userAtom)
    return `Hello, ${user.name}!`
  })
)
// Type: Atom<Result.Result<string, never>>
get.result automatically suspends when the atom is in a waiting state and fails when the atom has an error.

Chaining transformations

Build complex derivations by chaining transformations:
import { Atom } from "@effect-atom/atom"

const numbersAtom = Atom.make([1, 2, 3, 4, 5])

const statsAtom = numbersAtom.pipe(
  Atom.map(nums => nums.filter(n => n > 2)),
  Atom.map(nums => ({
    count: nums.length,
    sum: nums.reduce((a, b) => a + b, 0),
    average: nums.reduce((a, b) => a + b, 0) / nums.length
  }))
)

Conditional derivations

Use the get function for conditional logic:
import { Atom } from "@effect-atom/atom"

const isLoggedInAtom = Atom.make(false)
const userAtom = Atom.make({ name: "Alice" })
const guestNameAtom = Atom.make("Guest")

const displayNameAtom = Atom.make((get) => {
  const isLoggedIn = get(isLoggedInAtom)
  
  if (isLoggedIn) {
    const user = get(userAtom)
    return user.name
  } else {
    return get(guestNameAtom)
  }
})

Async derived state

Create derived atoms that perform async operations:
import { Atom } from "@effect-atom/atom"
import { Effect } from "effect"

const userIdAtom = Atom.make(1)

const userDetailsAtom = Atom.make((get) => {
  const userId = get(userIdAtom)
  
  return Effect.gen(function* () {
    // Fetch user details based on ID
    const response = yield* Effect.tryPromise(() =>
      fetch(`/api/users/${userId}`)
    )
    const data = yield* Effect.tryPromise(() => response.json())
    return data
  })
})
// Type: Atom<Result.Result<UserData, Error>>
When userIdAtom changes, userDetailsAtom automatically refetches with the new ID.

Transform combinator

For advanced cases, use Atom.transform to create custom derivations:
import { Atom } from "@effect-atom/atom"

const countAtom = Atom.make(0)

const customAtom = Atom.transform(countAtom, (get) => {
  const count = get(countAtom)
  
  // Access the atom's previous value
  const previous = get.self<number>()
  
  // Add finalizers
  get.addFinalizer(() => {
    console.log("Cleaning up")
  })
  
  return count * 2
})
transform gives you access to the full Context API, allowing you to:
  • Access other atoms with get
  • Get the atom’s own state with get.self()
  • Add cleanup logic with get.addFinalizer
  • Subscribe to other atoms
  • Mount dependencies

Dependency tracking

Atoms automatically track which other atoms they depend on:
import { Atom } from "@effect-atom/atom"

const aAtom = Atom.make(1)
const bAtom = Atom.make(2)
const cAtom = Atom.make(3)

const sumAtom = Atom.make((get) => {
  // This atom depends on all three
  return get(aAtom) + get(bAtom) + get(cAtom)
})

// sumAtom will update when ANY of a, b, or c change
Dependencies are tracked dynamically:
import { Atom } from "@effect-atom/atom"

const modeAtom = Atom.make<"a" | "b">("a")
const valueAAtom = Atom.make(1)
const valueBAtom = Atom.make(2)

const dynamicAtom = Atom.make((get) => {
  const mode = get(modeAtom)
  
  // Dependencies change based on mode
  if (mode === "a") {
    return get(valueAAtom)
  } else {
    return get(valueBAtom)
  }
})

// When mode is "a", only depends on modeAtom and valueAAtom
// When mode is "b", only depends on modeAtom and valueBAtom

Memoization

Derived atoms are automatically memoized - they only recalculate when dependencies change:
import { Atom } from "@effect-atom/atom"

let computeCount = 0

const sourceAtom = Atom.make(1)
const expensiveAtom = Atom.make((get) => {
  computeCount++
  const value = get(sourceAtom)
  // Expensive computation
  return value * 1000
})

// Reading multiple times doesn't recompute
const value1 = registry.get(expensiveAtom) // computeCount = 1
const value2 = registry.get(expensiveAtom) // computeCount = 1 (cached)

// Only recomputes when dependency changes
registry.set(sourceAtom, 2) // computeCount = 2

Reading without subscribing

Use get.once to read a value without creating a dependency:
import { Atom } from "@effect-atom/atom"

const timestampAtom = Atom.make(Date.now())
const dataAtom = Atom.make("data")

const loggedAtom = Atom.make((get) => {
  const data = get(dataAtom) // Creates dependency
  
  // Read timestamp once without creating dependency
  const timestamp = get.once(timestampAtom)
  
  console.log(`[${timestamp}] ${data}`)
  return data
})

// This atom only updates when dataAtom changes
// Changes to timestampAtom won't trigger updates

Writable derived atoms

Create two-way bindings with custom write logic:
import { Atom } from "@effect-atom/atom"

const celsiusAtom = Atom.make(0)

const fahrenheitAtom = Atom.writable(
  (get) => {
    const celsius = get(celsiusAtom)
    return (celsius * 9/5) + 32
  },
  (ctx, fahrenheit) => {
    const celsius = (fahrenheit - 32) * 5/9
    ctx.set(celsiusAtom, celsius)
  }
)

// Reading converts C to F
const f = registry.get(fahrenheitAtom)

// Writing converts F to C and updates source
registry.set(fahrenheitAtom, 100)

Build docs developers (and LLMs) love