MENU
useRouteError
When a route's loader, action, or own rendering throws, React Router doesn't crash the whole app – it renders that route's nearest error boundary instead. A route object opts into this with an ErrorBoundary field (a component, rendered in place of "element" when something throws) or, in the older style, an errorElement field (a plain element, e.g. errorElement={<RootErrorBoundary />}); both are still supported. Since an ErrorBoundary component isn't passed the error as a prop, it calls useRouteError() to read whatever was thrown. Like the rest of the hooks in this section, all of this only applies underneath a data router (see the Routers section).
useRouteError() can return anything – whatever the loader, action, or render actually threw. If your own code intentionally throws data("some message", { status: 404 }) (using the data() helper from "react-router") to signal something like a missing record, isRouteErrorResponse() lets your error boundary tell that case apart from a genuine bug, which is more likely to be a plain Error instance with a message and a stack.
import React from "react";
import {
createBrowserRouter, Link, Outlet, useLoaderData,
useRouteError, isRouteErrorResponse, data,
} from "react-router";
import { RouterProvider } from "react-router/dom";
const users = [
{ id: "1", name: "Grace Hopper" },
{ id: "2", name: "Ada Lovelace" },
];
function Layout() {
return (
<div>
<ul>
<li><Link to="/users/1">Grace Hopper</Link></li>
<li><Link to="/users/2">Ada Lovelace</Link></li>
<li><Link to="/users/99">Missing user</Link></li>
</ul>
<Outlet />
</div>
);
}
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{
path: "users/:id",
element: <UserProfile />,
ErrorBoundary: UserError,
loader: async ({ params }) => {
let user = users.find((u) => u.id === params.id);
if (!user) {
throw data("No user found with that id", { status: 404 });
}
return user;
},
},
],
},
]);
export default function UseRouteErrorExample() {
return <RouterProvider router={router} />;
}
function UserProfile() {
let user = useLoaderData();
return <h3>{user.name}</h3>;
}
function UserError() {
let error = useRouteError();
if (isRouteErrorResponse(error)) {
return (
<p>
{error.status} {error.statusText}: {error.data}
</p>
);
}
return <p>Something went wrong: {error instanceof Error ? error.message : "Unknown error"}</p>;
}Clicking "Missing user" runs the users/:id loader with a params.id of "99", which finds no match and throws a 404 data() response; UserError renders the isRouteErrorResponse() branch, showing its status, statusText, and the message passed to data(). Any other unexpected throw (say, a bug in UserProfile itself) falls into the plain Error branch instead. A route with no ErrorBoundary or errorElement of its own lets the error bubble up to the closest parent route that has one.