v0 · Developer Preview Frond is under active development. APIs may change between releases.

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.

HookSuspendsThrows readiness error
useNodeYesYes
useNodeStateYesYes
useNodes / PreloadYesYes
useNodeControlsNoNo

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>;
}
FieldDescription
headline, summary, messageHuman-readable text.
kindThe error projection kind.
retryableWhether retrying may help.
nodeId, nodeTagThe node that failed, if applicable.
operation, dependencyWhich 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.