Skip to content
IC Reactor

useActorSuspenseQuery

useActorSuspenseQuery is a React hook for fetching data from canisters with Suspense support. It suspends the component until data is available, eliminating the need for loading state handling.

import { createActorHooks } from "@ic-reactor/react"
import { backend } from "./reactor"
const { useActorSuspenseQuery } = createActorHooks(backend)
import { Suspense } from "react"
import { useActorSuspenseQuery } from "./reactor"
function UserProfile({ userId }: { userId: string }) {
// This hook suspends until data is loaded
const { data } = useActorSuspenseQuery({
functionName: "get_user",
args: [userId],
})
// data is ALWAYS defined here - no undefined check needed
return (
<div>
<h1>{data.name}</h1>
<p>{data.email}</p>
</div>
)
}
// Must be wrapped in Suspense
function App() {
return (
<Suspense fallback={<Loading />}>
<UserProfile userId="user-123" />
</Suspense>
)
}

UseActorSuspenseQueryParameters extends react-query’s QueryObserverOptions, giving you access to all standard react-query options plus our custom actor options.

Same as useActorQuery except:

Option Type Default Description
functionName string Required The canister method to call
args array [] Arguments to pass to the method
staleTime number 1000 Time in ms before data is considered stale. TanStack Query raises any lower value, 0 included, to 1 s
select function - Transform the data before returning

Returns a TanStack Query suspense result. The key difference from useActorQuery:

Property Type Description
data TData Always defined (never undefined)
error TError | null A failed first load is thrown to the Error Boundary. A failed refetch keeps data and sets this
status 'success' | 'error' 'error' after a failed refetch; the component suspends until there is data
import { Suspense } from "react"
import { useActorSuspenseQuery } from "./reactor"
function Greeting() {
const { data } = useActorSuspenseQuery({
functionName: "greet",
args: ["World"],
})
// data is guaranteed to be defined
return <h1>{data}</h1>
}
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Greeting />
</Suspense>
)
}

Two suspense queries in the same component run one after the other: the first hook suspends the component, so the second call starts only once the first has returned. Give each query its own component under one boundary and they start together:

import { Suspense } from "react"
import { useActorSuspenseQuery } from "./reactor"
function Dashboard({ userId }: { userId: string }) {
// Sibling components suspend separately, so both calls start at once
return (
<Suspense fallback={<Loading />}>
<Welcome userId={userId} />
<UserStats userId={userId} />
</Suspense>
)
}
function Welcome({ userId }: { userId: string }) {
const { data: user } = useActorSuspenseQuery({
functionName: "get_user",
args: [userId],
})
return <h1>Welcome, {user.name}</h1>
}
function UserStats({ userId }: { userId: string }) {
const { data: stats } = useActorSuspenseQuery({
functionName: "get_user_stats",
args: [userId],
})
return <Stats data={stats} />
}
import { useActorSuspenseQuery } from "./reactor"
function Hello({ userId }: { userId: string }) {
const { data: userName } = useActorSuspenseQuery({
functionName: "get_user",
args: [userId],
select: (user) => user.name, // data is just the name string
})
return <h1>Hello, {userName}</h1>
}
import { Suspense } from "react"
function App() {
return (
<Suspense fallback={<PageSkeleton />}>
<Header /> {/* Has its own data */}
<Suspense fallback={<ContentSkeleton />}>
<MainContent /> {/* Independent loading */}
</Suspense>
<Suspense fallback={<SidebarSkeleton />}>
<Sidebar /> {/* Independent loading */}
</Suspense>
</Suspense>
)
}
import { Suspense } from "react"
import { ErrorBoundary, type FallbackProps } from "react-error-boundary"
function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
return (
<div className="error">
<p>Something went wrong:</p>
<pre>{error instanceof Error ? error.message : String(error)}</pre>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
)
}
function App() {
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
<Suspense fallback={<Loading />}>
<UserProfile userId="user-123" />
</Suspense>
</ErrorBoundary>
)
}
import { useActorSuspenseQuery } from "./reactor"
function Settings() {
const { data } = useActorSuspenseQuery({
functionName: "get_config",
args: [],
staleTime: 5 * 60 * 1000, // Fresh for 5 minutes
})
return <pre>{JSON.stringify(data)}</pre>
}
import { useActorSuspenseQuery } from "./reactor"
function UserPosts({ userId }: { userId: string }) {
// This query runs first
const { data: user } = useActorSuspenseQuery({
functionName: "get_user",
args: [userId],
})
// This query runs after user is loaded
const { data: posts } = useActorSuspenseQuery({
functionName: "get_user_posts",
args: [user.id], // Uses data from first query
})
return <PostList posts={posts} author={user.name} />
}

✅ Use Suspense when:

  • You always need the data (no conditional fetching)
  • You want cleaner components without loading state
  • You want loading states at the boundary level

❌ Use regular useActorQuery when:

  • You need conditional fetching (enabled option)
  • You want per-component loading UI
  • You need to access isPending or isFetching states