Building a Multi-Page SPA with React RouterEntry 13 of 20

Error Handling with errorElement and useRouteError

When a loader, action, or component throws, React Router renders the nearest errorElement. Inside it, useRouteError() returns what was thrown.

Notes

  • Errors bubble up to the closest ancestor route with an errorElement, so one on the root route covers the whole app.
  • The errorElement replaces that route's element, so include navigation in the error page if you want it visible.
  • Throw data(payload, { status }) from a loader to send a status code and message. isRouteErrorResponse(error) detects it; read error.status and error.data.
  • A plain throw new Error(...) reaches the error page as an Error instance.
  • An unknown URL also renders the root errorElement (as a 404 route error response) if there's no * route.

What / Why / When

What

Route-scoped error boundaries.

Why

A failing request shows a friendly page instead of crashing the app.

When

Always at the root; add more on routes that need a more specific message.

Code compare

React
// pages/Events.tsximport { data } from "react-router";​export async function loader() {  try {    return await getEvents();  } catch {    throw data({ message: "Could not fetch events." }, { status: 500 });  }}
React
// pages/Error.tsximport { isRouteErrorResponse, useRouteError } from "react-router";import MainNavigation from "../components/MainNavigation";​export default function ErrorPage() {  const error = useRouteError();​  let title = "An error occurred!";  let message = "Something went wrong!";​  if (isRouteErrorResponse(error)) {    if (error.status === 404) {      title = "Not found!";      message = "Could not find resource or page.";    } else {      message = error.data?.message ?? message;    }  } else if (error instanceof Error) {    message = error.message;  }​  return (    <>      <MainNavigation />      <main>        <h1>{title}</h1>        <p>{message}</p>      </main>    </>  );}
React
// App.tsx{ path: '/', element: <RootLayout />, errorElement: <ErrorPage />, children: [/* ... */] }

Angular has no route-level error boundary. Handle errors in the resolver and redirect to an error page, or register a global handler with withNavigationErrorHandler().

Angular
// events.resolver.tsexport const eventsResolver: ResolveFn<EventItem[]> = () => {  const router = inject(Router);​  return inject(EventsService)    .getEvents()    .pipe(      catchError(() =>        of(          new RedirectCommand(router.parseUrl("/error?message=Could not fetch events."), {            skipLocationChange: true,          }),        ),      ),    );};
Angular
// error.component.ts@Component({  selector: "app-error",  template: `    <h1>An error occurred!</h1>    <p>{{ message() ?? "Something went wrong!" }}</p>  `,})export class ErrorComponent {  message = input<string>(); // bound from ?message=}

Differences and gotchas

  • React's errorElement renders in place at the failing route. Angular redirects to a separate error route (skipLocationChange keeps the original URL visible).
  • Unexpected errors elsewhere in Angular go to the global ErrorHandler.

Quick reference

ReactAngular
errorElementError route + RedirectCommand / withNavigationErrorHandler()
useRouteError()Error data via inputs / navigation state
throw data(msg, { status })catchError in resolver
Bubbles to nearest ancestorGlobal ErrorHandler for the rest