Suspense and errors
The reading hooks suspend until their node is ready and throw readiness failures to the nearest error boundary. So a screen using Frond needs two boundaries above it: a Suspense for loading and an error boundary for failure.
| Hook | Suspends | Throws readiness error |
|---|---|---|
useNode | Yes | Yes |
useNodeState | Yes | Yes |
useNodes / Preload | Yes | Yes |
useNodeControls | No | No |
useNodeControls reads no result, so it neither suspends nor throws.
Boundaries
Put a Suspense and an error boundary above the suspending components. One pair can cover a whole screen — group acquisitions with Preload so the screen has a single loading and a single error state.
import { Suspense } from "react";
import { ErrorBoundary } from "react-error-boundary";
<ErrorBoundary FallbackComponent={Fallback}>
<Suspense fallback={<Loading />}>
<ProfileScreen userId="u_42" />
</Suspense>
</ErrorBoundary>;
Reading the error
getErrorReport(error) projects any caught error into a structured, displayable report. Projection keeps Frond’s graph context while surfacing the real root cause for display or reporting. See Error projection.
import { getErrorReport } from "@frondruntime/react";
function Fallback({ error }: { error: unknown }) {
const report = getErrorReport(error);
return <p>{report.headline}</p>;
}
| Field | Description |
|---|---|
headline, summary, message | Human-readable text. |
kind | The error projection kind. |
retryable | Whether retrying may help. |
nodeId, nodeTag | The node that failed, if applicable. |
operation, dependency | Which operation or dependency failed. |
Retrying
A readiness failure is retryable. getErrorRecovery(error) returns a recovery with a retry callback when the error is recoverable, or undefined otherwise. isRecoverableNodeError(error) is the boolean check.
import { getErrorRecovery, getErrorReport } from "@frondruntime/react";
function Fallback({ error, resetErrorBoundary }: FallbackProps) {
const report = getErrorReport(error);
const recovery = getErrorRecovery(error);
return (
<div>
<p>{report.headline}</p>
{recovery?.retryable && (
<button onClick={() => void recovery.retry().finally(resetErrorBoundary)}>
Retry
</button>
)}
</div>
);
}
recovery.retry() starts a fresh readiness attempt; resetting the boundary re-renders the subtree, which re-reads the now-loading node and suspends again.
Operation failures do not throw here
Only readiness failures reach the error boundary. A failed refresh or action leaves the node ready and surfaces as an operation failure, not a thrown error. Read it from useNodeState’s operationFailure, or handle the rejected action promise where you called it. See Errors.
Next: Sentry projection.