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.
Overview
The useAtomSuspense hook reads atoms that contain Result values and integrates with React Suspense. It automatically suspends the component while the async operation is pending and throws errors in the failure case (unless configured otherwise).
Signature
export const useAtomSuspense: <A, E, const IncludeFailure extends boolean = false>(
atom: Atom.Atom<Result.Result<A, E>>,
options?: {
readonly suspendOnWaiting?: boolean | undefined
readonly includeFailure?: IncludeFailure | undefined
}
) => Result.Success<A, E> | (IncludeFailure extends true ? Result.Failure<A, E> : never)
Parameters
atom
Atom.Atom<Result.Result<A, E>>
required
An atom that contains a Result value. This is typically created with Atom.makeWithEffect or other async atom constructors.
Configuration options for Suspense behavior.If true, the component will suspend when the result is in a “waiting” state (e.g., during background revalidation). Defaults to false.
If true, failure results are returned instead of thrown as errors. This allows you to handle errors in the component. Defaults to false.
Returns
result
Result.Success<A, E> | Result.Failure<A, E>
The result value from the atom:
- When
includeFailure is false (default): Returns only Result.Success<A, E>. Failures are thrown as exceptions.
- When
includeFailure is true: Returns either Result.Success<A, E> or Result.Failure<A, E>.
- Suspends: When the result is
Initial or (if suspendOnWaiting: true) when waiting is true.
Usage
Basic async data fetching
Fetch data with automatic Suspense:
import { useAtomSuspense } from "@effect-atom/atom-react"
import { Atom } from "@effect-atom/atom"
import { Effect } from "effect"
import { Suspense } from "react"
const userAtom = Atom.makeWithEffect((get) =>
Effect.gen(function* () {
const response = yield* Effect.tryPromise(() =>
fetch("/api/user")
)
return yield* Effect.tryPromise(() => response.json())
})
)
function UserProfile() {
const result = useAtomSuspense(userAtom)
return (
<div>
<h1>{result.value.name}</h1>
<p>{result.value.email}</p>
</div>
)
}
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<UserProfile />
</Suspense>
)
}
Handling errors with includeFailure
Manage both success and failure states:
import { useAtomSuspense } from "@effect-atom/atom-react"
import { Atom } from "@effect-atom/atom"
import { Effect } from "effect"
import { Suspense } from "react"
const dataAtom = Atom.makeWithEffect((get) =>
Effect.gen(function* () {
const response = yield* Effect.tryPromise({
try: () => fetch("/api/data"),
catch: (error) => ({ _tag: "FetchError" as const, error })
})
if (!response.ok) {
return yield* Effect.fail({ _tag: "ApiError" as const, status: response.status })
}
return yield* Effect.tryPromise({
try: () => response.json(),
catch: (error) => ({ _tag: "ParseError" as const, error })
})
})
)
function DataDisplay() {
const result = useAtomSuspense(dataAtom, { includeFailure: true })
if (result._tag === "Failure") {
return <div className="error">Failed to load data</div>
}
return (
<div>
<pre>{JSON.stringify(result.value, null, 2)}</pre>
</div>
)
}
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<DataDisplay />
</Suspense>
)
}
Suspending on background updates
Suspend during revalidation:
import { useAtomSuspense } from "@effect-atom/atom-react"
import { Atom } from "@effect-atom/atom"
import { Effect } from "effect"
import { Suspense } from "react"
const pollAtom = Atom.makeWithEffect(
(get) => fetchLatestData(),
{ refreshInterval: 5000 } // Refresh every 5 seconds
)
function LiveData() {
// Will suspend on both initial load and background refreshes
const result = useAtomSuspense(pollAtom, { suspendOnWaiting: true })
return <div>Latest: {result.value}</div>
}
function App() {
return (
<Suspense fallback={<div>Updating...</div>}>
<LiveData />
</Suspense>
)
}
Showing stale data during revalidation
Keep showing data while refreshing:
import { useAtomSuspense } from "@effect-atom/atom-react"
import { Atom } from "@effect-atom/atom"
import { Effect } from "effect"
import { Suspense } from "react"
const dataAtom = Atom.makeWithEffect((get) => fetchData())
function DataWithRefreshIndicator() {
// Don't suspend on waiting - show stale data instead
const result = useAtomSuspense(dataAtom, { suspendOnWaiting: false })
return (
<div>
<div className="data">
{result.value}
</div>
{result.waiting && (
<span className="badge">Refreshing...</span>
)}
</div>
)
}
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<DataWithRefreshIndicator />
</Suspense>
)
}
Error boundary integration
Use Error Boundaries to catch failures:
import { useAtomSuspense } from "@effect-atom/atom-react"
import { Atom } from "@effect-atom/atom"
import { Effect } from "effect"
import { Suspense, Component } from "react"
class ErrorBoundary extends Component {
state = { hasError: false, error: null }
static getDerivedStateFromError(error) {
return { hasError: true, error }
}
render() {
if (this.state.hasError) {
return <div>Error: {this.state.error.message}</div>
}
return this.props.children
}
}
const riskyAtom = Atom.makeWithEffect((get) =>
Effect.gen(function* () {
const data = yield* fetchRiskyData()
if (!data) {
return yield* Effect.fail(new Error("No data available"))
}
return data
})
)
function RiskyComponent() {
// Failures will be thrown and caught by ErrorBoundary
const result = useAtomSuspense(riskyAtom)
return <div>{result.value}</div>
}
function App() {
return (
<ErrorBoundary>
<Suspense fallback={<div>Loading...</div>}>
<RiskyComponent />
</Suspense>
</ErrorBoundary>
)
}
Multiple async dependencies
Combine multiple async atoms:
import { useAtomSuspense } from "@effect-atom/atom-react"
import { Atom } from "@effect-atom/atom"
import { Effect } from "effect"
import { Suspense } from "react"
const userAtom = Atom.makeWithEffect((get) => fetchUser())
const postsAtom = Atom.makeWithEffect((get) => fetchPosts())
function UserDashboard() {
const user = useAtomSuspense(userAtom)
const posts = useAtomSuspense(postsAtom)
return (
<div>
<h1>{user.value.name}'s Posts</h1>
<ul>
{posts.value.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
)
}
function App() {
return (
<Suspense fallback={<div>Loading dashboard...</div>}>
<UserDashboard />
</Suspense>
)
}
Best practices
Always wrap components using useAtomSuspense with a Suspense boundary. Without one, React will throw an error.
Use suspendOnWaiting: false (default) to show stale data during background revalidation. This provides a better user experience than showing a loading spinner for updates.
By default, failures are thrown as exceptions. Use Error Boundaries to catch them, or set includeFailure: true to handle errors in your component.
This hook only works with atoms that contain Result values. For regular atoms, use useAtomValue or useAtom instead.