Skip to content

Error Boundaries

StateMesh provides MeshErrorBoundary and MeshErrorResetBoundary for handling errors from Suspense resource reads, transaction failures, and other async operations.

MeshErrorBoundary

Wrap components that may throw (e.g., Suspense resource reads):

tsx
import { Suspense } from "react";
import { MeshErrorBoundary, useSuspenseMeshResource } from "statemesh-core";

function Products() {
  const products = useSuspenseMeshResource(productsResource, filters);
  return products.data.map((p) => <div key={p.id}>{p.name}</div>);
}

function ProductsRoute() {
  return (
    <MeshErrorBoundary
      fallbackRender={({ error, reset }) => (
        <div>
          <p>Error: {error.message}</p>
          <button onClick={reset}>Retry</button>
        </div>
      )}
    >
      <Suspense fallback={<p>Loading...</p>}>
        <Products />
      </Suspense>
    </MeshErrorBoundary>
  );
}

Props

PropTypeDescription
fallbackReactNodeStatic fallback UI
fallbackRender({ error, reset }) => ReactNodeDynamic fallback with error and reset
onError(error, info) => voidCalled when an error is caught

Reset Boundary

Use MeshErrorResetBoundary to control when the error boundary resets:

tsx
import { MeshErrorResetBoundary, useMeshErrorResetBoundary } from "statemesh-core";

function ResettableSection() {
  const { reset } = useMeshErrorResetBoundary();

  return (
    <MeshErrorResetBoundary>
      <MeshErrorBoundary fallbackRender={({ error }) => (
        <div>
          <p>{error.message}</p>
          <button onClick={reset}>Try again</button>
        </div>
      )}>
        <Suspense fallback={<p>Loading...</p>}>
          <AsyncComponent />
        </Suspense>
      </MeshErrorBoundary>
    </MeshErrorResetBoundary>
  );
}

useMeshErrorResetBoundary

ts
const { reset } = useMeshErrorResetBoundary();

Call reset() to clear the error state and re-render the children. This forces the Suspense boundary to retry the async operation.

Pattern: Route Error Boundary

tsx
function RouteErrorBoundary({ children }: { children: React.ReactNode }) {
  return (
    <MeshErrorBoundary
      fallbackRender={({ error, reset }) => (
        <div className="error-page">
          <h1>Something went wrong</h1>
          <p>{error.message}</p>
          <button onClick={reset}>Try again</button>
          <button onClick={() => window.location.href = "/"}>Go home</button>
        </div>
      )}
      onError={(error) => {
        console.error("Route error:", error);
        analytics.track("error", { message: error.message });
      }}
    >
      {children}
    </MeshErrorBoundary>
  );
}

Pattern: Section Error Boundary

tsx
function ProductSection() {
  return (
    <MeshErrorBoundary
      fallbackRender={({ error, reset }) => (
        <div className="section-error">
          <p>Failed to load products: {error.message}</p>
          <button onClick={reset}>Retry</button>
        </div>
      )}
    >
      <Suspense fallback={<ProductSkeleton />}>
        <ProductList />
      </Suspense>
    </MeshErrorBoundary>
  );
}

Important Notes

TIP

MeshErrorBoundary catches errors from useSuspenseMeshResource, transaction failures thrown during render, and any other errors thrown in child components.

WARNING

The reset callback forces a re-render of the children. If the underlying error persists (e.g., the API is still down), the error boundary will catch the new error and show the fallback again.

TIP

Use multiple nested error boundaries for granular error handling. A product card can fail independently without taking down the entire page.

Next Steps

Released under the MIT License.