Skip to content

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"
const { useActorSuspenseQuery } = createActorHooks(backend)
import { Suspense } from "react"
function UserProfile({ userId }: { userId: string }) {
// This hook suspends until data is loaded
const { data } = useActorSuspenseQuery({
functionName: "getUser",
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>
)
}

Same as useActorQuery except:

OptionTypeDefaultDescription
functionNamestringRequiredThe canister method to call
argsarray[]Arguments to pass to the method
staleTimenumber0Time in ms before data is considered stale
selectfunction-Transform the data before returning

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

PropertyTypeDescription
dataTDataAlways defined (never undefined)
errorneverErrors are thrown, not returned
status'success'Always success (suspends otherwise)
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>
)
}
function Dashboard({ userId }: { userId: string }) {
// Both queries run in parallel
const { data: user } = useActorSuspenseQuery({
functionName: "getUser",
args: [userId],
})
const { data: stats } = useActorSuspenseQuery({
functionName: "getUserStats",
args: [userId],
})
return (
<div>
<h1>Welcome, {user.name}</h1>
<Stats data={stats} />
</div>
)
}
const { data: userName } = useActorSuspenseQuery({
functionName: "getUser",
args: [userId],
select: (user) => user.name, // data is just the name string
})
return <h1>Hello, {userName}</h1>
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 { ErrorBoundary } from "react-error-boundary"
function ErrorFallback({ error, resetErrorBoundary }) {
return (
<div className="error">
<p>Something went wrong:</p>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
)
}
function App() {
return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
<Suspense fallback={<Loading />}>
<UserProfile userId="user-123" />
</Suspense>
</ErrorBoundary>
)
}
const { data } = useActorSuspenseQuery({
functionName: "getConfig",
args: [],
staleTime: 5 * 60 * 1000, // Fresh for 5 minutes
})
function UserPosts({ userId }: { userId: string }) {
// This query runs first
const { data: user } = useActorSuspenseQuery({
functionName: "getUser",
args: [userId],
})
// This query runs after user is loaded
const { data: posts } = useActorSuspenseQuery({
functionName: "getUserPosts",
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’re using React Server Components
  • 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