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.

The @effect-atom/atom-solid package provides Solid primitives for integrating Effect Atom into your SolidJS applications.

Installation

pnpm add @effect-atom/atom-solid

Core primitives

useAtomValue

Read the current value of an atom and subscribe to changes.
import { Atom, useAtomValue } from "@effect-atom/atom-solid"

const countAtom = Atom.make(0)

function Counter() {
  const count = useAtomValue(countAtom)
  return <h1>{count()}</h1>
}
useAtomValue returns a Solid Accessor that automatically updates when the atom value changes.

Transform values

You can pass a transformation function to derive a new value:
function DoubleCounter() {
  const doubled = useAtomValue(countAtom, (count) => count * 2)
  return <h1>{doubled()}</h1>
}

useAtomSet

Get a setter function for an atom without subscribing to its value.
import { Atom, useAtomSet } from "@effect-atom/atom-solid"

const countAtom = Atom.make(0)

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

Mode options

For atoms that return Result types, you can specify how to handle the result:
function Component() {
  const setCount = useAtomSet(effectfulAtom)
  // Returns void immediately
  setCount(42)
}

useAtom

Combines useAtomValue and useAtomSet for reading and writing in a single primitive.
import { Atom, useAtom } from "@effect-atom/atom-solid"

const countAtom = Atom.make(0)

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

useAtomRefresh

Get a function to manually refresh an atom’s value.
import { Atom, useAtomValue, useAtomRefresh } from "@effect-atom/atom-solid"
import { Effect } from "effect"
import { Show } from "solid-js"

const timeAtom = Atom.make(Effect.sync(() => Date.now()))

function CurrentTime() {
  const result = useAtomValue(timeAtom)
  const refresh = useAtomRefresh(timeAtom)
  
  return (
    <div>
      <Show when={result()._tag === "Success"}>
        {(r) => <p>Current time: {r().value}</p>}
      </Show>
      <button onClick={refresh}>Refresh</button>
    </div>
  )
}

Additional primitives

useAtomMount

Mount an atom without reading or writing its value. Useful for atoms with side effects.
import { Atom, useAtomMount } from "@effect-atom/atom-solid"

const analyticsAtom = Atom.make(
  Effect.gen(function* () {
    yield* Effect.log("Component mounted")
    yield* Effect.addFinalizer(() => Effect.log("Component unmounted"))
  })
)

function TrackedComponent() {
  useAtomMount(analyticsAtom)
  return <div>This component is tracked</div>
}

useAtomSubscribe

Subscribe to atom changes with a custom callback.
import { Atom, useAtomSubscribe } from "@effect-atom/atom-solid"

const countAtom = Atom.make(0)

function Logger() {
  useAtomSubscribe(
    countAtom,
    (count) => console.log("Count changed:", count),
    { immediate: true }
  )
  return null
}

useAtomRef

Subscribe to an AtomRef value.
import { AtomRef, useAtomRef } from "@effect-atom/atom-solid"

const formRef = AtomRef.make({ name: "", email: "" })

function FormDisplay() {
  const form = useAtomRef(formRef)
  return <div>{form().name} - {form().email}</div>
}

useAtomInitialValues

Set initial values for atoms on the server or during hydration.
import { Atom, useAtomInitialValues } from "@effect-atom/atom-solid"

interface User {
  id: string
  name: string
}

const userAtom = Atom.make<User | null>(null)

function App(props: { initialUser: User }) {
  useAtomInitialValues([[userAtom, props.initialUser]])
  // ...
}

Registry context

By default, all atoms share a global registry. You can create isolated registries using RegistryProvider.

RegistryProvider

Provide a custom registry to a component tree.
import { RegistryProvider } from "@effect-atom/atom-solid"

function App() {
  return (
    <RegistryProvider
      defaultIdleTTL={1000}
      initialValues={[[countAtom, 10]]}
    >
      <Counter />
    </RegistryProvider>
  )
}

Props

  • children: JSX elements to render
  • initialValues: Initial atom values as [atom, value] pairs
  • scheduleTask: Custom task scheduler function
  • timeoutResolution: Timeout resolution in milliseconds
  • defaultIdleTTL: Default time-to-live for idle atoms in milliseconds
Each RegistryProvider creates an isolated atom state scope. Atoms are not shared between different registries.

RegistryContext

Access the current registry directly.
import { RegistryContext } from "@effect-atom/atom-solid"
import { useContext } from "solid-js"

function CustomPrimitive() {
  const registry = useContext(RegistryContext)
  // Use registry directly
}

Working with effects

Solid primitives work seamlessly with Effect atoms.
import { Atom, Result, useAtomValue } from "@effect-atom/atom-solid"
import { Effect } from "effect"
import { Show, createMemo } from "solid-js"

interface User {
  id: string
  name: string
}

const userAtom = Atom.make(
  Effect.gen(function* () {
    const response = yield* Effect.tryPromise(() =>
      fetch("/api/user").then(r => r.json())
    )
    return response as User
  })
)

function UserProfile() {
  const result = useAtomValue(userAtom)
  const user = createMemo(() => Result.getOrElse(result(), () => null))
  
  return (
    <Show when={user()} fallback={<div>Loading...</div>}>
      {(u) => (
        <div>
          <h2>{u().name}</h2>
          <p>ID: {u().id}</p>
        </div>
      )}
    </Show>
  )
}

Working with streams

Effect Atom works seamlessly with Effect streams for reactive data sources.
import { Atom, Result, useAtomValue } from "@effect-atom/atom-solid"
import { Schedule, Stream, Cause } from "effect"
import { Show, createMemo } from "solid-js"

const countAtom = Atom.make(Stream.fromSchedule(Schedule.spaced(1000)))

function StreamCounter() {
  const result = useAtomValue(countAtom)
  
  return (
    <Show
      when={result()._tag === "Success"}
      fallback={
        <Show when={result()._tag === "Failure"}>
          {(r) => <div>Error: {Cause.pretty(r().cause)}</div>}
        </Show>
      }
    >
      {(r) => <div>Count: {r().value}</div>}
    </Show>
  )
}

Pull-based streams

For paginated or infinite scroll data:
import { Atom, Result, useAtom } from "@effect-atom/atom-solid"
import { Stream } from "effect"
import { Show, For, createMemo } from "solid-js"

const itemsAtom = Atom.pull(Stream.range(1, 100))

function InfiniteList() {
  const [result, loadMore] = useAtom(itemsAtom)
  
  const items = createMemo(() =>
    result()._tag === "Success" ? result().value.items : []
  )
  
  const done = createMemo(() =>
    result()._tag === "Success" ? result().value.done : false
  )
  
  return (
    <div>
      <ul>
        <For each={items()}>
          {(item) => <li>{item}</li>}
        </For>
      </ul>
      <Show when={!done()}>
        <button onClick={() => loadMore()}>Load more</button>
      </Show>
    </div>
  )
}

Complete example

import { Atom, useAtomValue, useAtomSet } from "@effect-atom/atom-solid"

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

function App() {
  return (
    <div>
      <Counter />
      <CounterButton />
    </div>
  )
}

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

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

SolidStart and SSR

Effect Atom supports server-side rendering with SolidStart.
import { Atom, useAtomValue } from "@effect-atom/atom-solid"
import { isServer } from "solid-js/web"

const dataAtom = Atom.make(
  Effect.gen(function* () {
    if (isServer) {
      // Server-side data fetching
      return yield* fetchFromDatabase()
    }
    // Client-side data fetching
    return yield* fetchFromAPI()
  })
)
Atoms with asynchronous effects return Result.Initial during SSR. Use Result.getOrElse to handle loading states.

Build docs developers (and LLMs) love