Motivation
Jotai’s async atoms work with Suspense, but hooks hide which atoms trigger Suspense and where. @suspensive/jotai makes this visible.
Hooks hide what triggers Suspense
When a child component uses useAtom with an async atom, the parent has no way to tell which children will suspend. Suspense boundaries become guesswork.
const PaymentPage = () => (
<Suspense fallback={'pending...'}>
{/* Does UserInfo suspend? Which atom? No way to tell from here. */}
<UserInfo />
<ShoppingCart />
</Suspense>
)With Suspensive’s <Atom/>, <AtomValue/>, and <SetAtom/>, atom usage is declared right in JSX — you can see exactly what suspends and where.
const PaymentPage = () => (
<Suspense fallback={'pending...'}>
{/* Clear: userAtom triggers Suspense here. */}
<Atom atom={userAtom}>{([data]) => <UserProfile {...data} />}</Atom>
<Atom atom={cartAtom}>{([data]) => <ShoppingCart {...data} />}</Atom>
</Suspense>
)Works with Jotai’s extension ecosystem
Jotai has extensions like tRPC , Query , and Cache . With Suspensive’s <Atom/>, <AtomValue/>, and <SetAtom/>, atoms from these extensions work out of the box — no extra wrappers needed.
import { AtomValue } from '@suspensive/jotai'
import { Suspense, ErrorBoundary } from '@suspensive/react'
import { userQueryAtom } from '~/queries' // atomWithSuspenseQuery from jotai-tanstack-query
const MyPage = () => (
<ErrorBoundary fallback={({ error }) => <>{error.message}</>}>
<Suspense fallback={'pending...'}>
<AtomValue atom={userQueryAtom}>
{({ data: user }) => <UserProfile key={user.id} {...user} />}
</AtomValue>
</Suspense>
</ErrorBoundary>
)Last updated on