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.

This guide will walk you through creating a simple counter application using Effect Atom. You’ll learn the core concepts while building something that works.

Prerequisites

  • Node.js 18 or higher
  • A React, Vue, or Solid project set up
  • Effect Atom installed (see Installation)

Build a counter

1

Create an atom

First, create an atom to hold the counter state. Atoms are reactive containers that automatically trigger updates when their value changes.
counter.tsx
import { Atom } from "@effect-atom/atom-react"

export const countAtom = Atom.make(0).pipe(
  // By default, atoms reset when no components use them.
  // keepAlive keeps the value across component unmounts.
  Atom.keepAlive
)
Atom.make(0) creates an atom with an initial value of 0. The type is automatically inferred as Atom.Atom<number>.
2

Read the atom value

Use the useAtomValue hook to read the current value in a component:
Counter.tsx
import { useAtomValue } from "@effect-atom/atom-react"
import { countAtom } from "./counter"

export function Counter() {
  const count = useAtomValue(countAtom)
  return <h1>{count}</h1>
}
The component automatically re-renders when countAtom changes.
3

Update the atom

Use useAtomSet to get a setter function:
CounterButton.tsx
import { useAtomSet } from "@effect-atom/atom-react"
import { countAtom } from "./counter"

export function CounterButton() {
  const setCount = useAtomSet(countAtom)

  return (
    <button onClick={() => setCount((count) => count + 1)}>
      Increment
    </button>
  )
}
The setter accepts either a new value or an updater function that receives the current value.
4

Compose your app

Put it together:
App.tsx
import { Counter } from "./Counter"
import { CounterButton } from "./CounterButton"

export function App() {
  return (
    <div>
      <Counter />
      <CounterButton />
    </div>
  )
}
Notice how Counter and CounterButton are in separate components but share the same state through countAtom. This is the power of atom-based state management.

Complete working example

Here’s the full counter implementation in a single file:
App.tsx
import { Atom, useAtomValue, useAtomSet } from "@effect-atom/atom-react"

// Create the atom
const countAtom = Atom.make(0).pipe(Atom.keepAlive)

// Component that displays the count
function Counter() {
  const count = useAtomValue(countAtom)
  return <h1>{count}</h1>
}

// Component that increments the count
function CounterButton() {
  const setCount = useAtomSet(countAtom)
  return (
    <button onClick={() => setCount((count) => count + 1)}>
      Increment
    </button>
  )
}

// Component that decrements the count
function DecrementButton() {
  const setCount = useAtomSet(countAtom)
  return (
    <button onClick={() => setCount((count) => count - 1)}>
      Decrement
    </button>
  )
}

// Component that resets the count
function ResetButton() {
  const setCount = useAtomSet(countAtom)
  return <button onClick={() => setCount(0)}>Reset</button>
}

// Main app
export function App() {
  return (
    <div>
      <Counter />
      <div style={{ display: "flex", gap: "8px" }}>
        <DecrementButton />
        <CounterButton />
        <ResetButton />
      </div>
    </div>
  )
}

Add derived state

Let’s add a derived atom that computes the doubled value:
import { Atom, useAtomValue, useAtomSet } from "@effect-atom/atom-react"

const countAtom = Atom.make(0).pipe(Atom.keepAlive)

// Create a derived atom that automatically updates
const doubleCountAtom = Atom.map(countAtom, (count) => count * 2)

function Counter() {
  const count = useAtomValue(countAtom)
  const doubled = useAtomValue(doubleCountAtom)
  
  return (
    <div>
      <h1>Count: {count}</h1>
      <h2>Doubled: {doubled}</h2>
    </div>
  )
}

function CounterButton() {
  const setCount = useAtomSet(countAtom)
  return (
    <button onClick={() => setCount((count) => count + 1)}>
      Increment
    </button>
  )
}

export function App() {
  return (
    <div>
      <Counter />
      <CounterButton />
    </div>
  )
}
doubleCountAtom automatically recomputes whenever countAtom changes. No manual subscription needed!

Alternative: Use the combined hook

You can also use useAtom to get both the value and setter:
import { Atom, useAtom } from "@effect-atom/atom-react"

const countAtom = Atom.make(0).pipe(Atom.keepAlive)

function Counter() {
  const [count, setCount] = useAtom(countAtom)
  
  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => setCount((count) => count + 1)}>
        Increment
      </button>
    </div>
  )
}

Key concepts recap

Atoms hold state and automatically notify subscribers when the value changes. Components using useAtomValue re-render when their atoms update.
Multiple components can share the same atom by importing the same atom instance. Changes in one component automatically reflect in others.
Use Atom.map or pass a function to Atom.make to create atoms that derive from other atoms. Dependencies are tracked automatically.
By default, atoms reset when no components use them. Use Atom.keepAlive to preserve state across unmounts.

Next steps

Core concepts

Learn about atoms, effects, and composition

Working with effects

Handle async operations with Effect

Derived state

Create complex derivations and computations

API reference

Explore the complete API

Build docs developers (and LLMs) love