Error Boundaries

View as Markdown

In Neutron, errors don't have to crash your entire application. You can define Error Boundaries to catch errors within specific routes or layouts.

The ErrorBoundary Component

Any route file can export an ErrorBoundary component.

// src/routes/app/dashboard.tsx
import type { ErrorBoundaryProps } from "@neutron-build/core";

export default function Dashboard() {
  throw new Error("Something broke!");
}

// The nearest ErrorBoundary catches the error. It receives the thrown error
// as a prop, plus an optional `reset` to retry rendering.
export function ErrorBoundary({ error, reset }: ErrorBoundaryProps) {
  return (
    <div className="error">
      <h1>Oops!</h1>
      <p>{error.message}</p>
      {reset && <button onClick={reset}>Try again</button>}
    </div>
  );
}

Granular Error Handling

Error boundaries operate hierarchically. If a route throws an error, Neutron looks for the nearest Error Boundary.

  1. Route level: src/routes/app/dashboard.tsx (exports ErrorBoundary)
  2. Sibling level: src/routes/app/_error.tsx (if the route file doesn't export one)
  3. Parent Layout: src/routes/app/_layout.tsx
  4. Root: src/routes/_error.tsx

This means if a specific part of your page crashes (e.g., a widget in the dashboard), the surrounding layout (sidebar, header) remains interactive. Only the broken part is replaced by the Error Boundary.

Expected Errors (Throwing Responses)

To short-circuit a request with a specific HTTP response — a 404, a redirect, a permission error — throw a Response from a loader or action (the notFound() and redirect() helpers return one). Neutron serves it directly as the HTTP response; it does not render the ErrorBoundary.

import { notFound, type LoaderArgs } from "@neutron-build/core";

export async function loader({ params }: LoaderArgs) {
  const project = await getProject(params.id);

  if (!project) {
    throw notFound(); // → 404 response, served as-is
  }

  return { project };
}

Throwing (or letting propagate) an Error — rather than a Response — is what triggers the nearest ErrorBoundary.