React & Modern Frontend (445 Qs)•Part 10: Modern React 18, React 19 & Next.js Patterns (317 - 353)
How do you handle 404 / Not Found routes?
React Router matches routes top-to-bottom/most-specific-first, so a catch-all route with path="*" placed last will match any URL that didn't match an earlier route — this is the v6+ equivalent of the older approach in How do you implement a default or NotFound page?.
jsx
import { Routes, Route } from "react-router-dom";
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/users/:userId" element={<UserProfile />} />
{/* Catch-all — must be last; matches any unmatched path */}
<Route path="*" element={<NotFound />} />
</Routes>
);
}
function NotFound() {
return <h1>404 — Page Not Found</h1>;
}
For apps using the newer data router APIs (createBrowserRouter), a route-level errorElement handles both unmatched paths and thrown errors/loader failures for that branch:
jsx
import { createBrowserRouter } from "react-router-dom";
const router = createBrowserRouter([
{
path: "/",
element: <Root />,
errorElement: <ErrorPage />, // renders on 404s and unhandled loader/action errors
children: [{ path: "users/:userId", element: <UserProfile />, loader: userLoader }],
},
]);
Key points:
- Always keep the
path="*"route last — route matching order matters, and an earlier catch-all would shadow every route after it. - Return a real HTTP 404 status from your server for true not-found responses when doing SSR, so search engines and monitoring tools see the correct status code (the client-only SPA route doesn't set the HTTP status by itself).
- Distinguish "route not found" (bad URL) from "resource not found" (valid route, but e.g.
/users/999doesn't exist) — the latter is usually handled inside the page component after a failed fetch/loader, not by the router itself.