A 404 page in React Router appears when the URL matches no route. You build it with a catch-all route whose path is *, placed last in the Routes list. This article covers the declarative mode and the data mode error boundary.
A dedicated page beats a blank screen. It tells visitors the link is dead and points them somewhere useful.
Add a catch-all route
A catch-all route uses * as its path, which matches any URL that the earlier routes did not. Place it last.
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<NotFound />} />
</Routes>When no earlier route matches, React Router renders NotFound. The order matters, because the catch-all accepts everything. If the Routes setup is new, the React Router tutorial covers it.
React Router ranks routes by specificity, but the catch-all is the fallback for anything with no match at all. Keep it last so a future route does not get swallowed.
The catch-all also works in nested trees, where it renders inside the parent layout when nested under that parent.
Build the NotFound component
The not found component is a normal page. It should tell the user what happened and offer a way back.
import { Link } from "react-router";
export default function NotFound() {
return (
<main>
<h1>Page not found</h1>
<p>The page you requested does not exist.</p>
<Link to="/">Back to home</Link>
</main>
);
}The Link sends the user to the home route. A heading and a short message keep the page useful instead of showing a blank screen.
You can echo the bad URL to help users report broken links. Read location.pathname with useLocation inside the component and render it in the message.
The path is only for display, so there is no need to store it in state.
Show the requested path
Echo the bad URL so users can report it. Read the path from useLocation inside the not found component.
import { useLocation } from "react-router";
export default function NotFound() {
const location = useLocation();
return <p>No page at {location.pathname}</p>;
}This small addition makes a 404 actionable instead of generic.
Show a 404 in data mode
In data mode, route modules can export an ErrorBoundary that reads the error with useRouteError. Check for a 404 response and render the same not found page.
import { useRouteError, isRouteErrorResponse } from "react-router";
export function ErrorBoundary() {
const error = useRouteError();
if (isRouteErrorResponse(error) && error.status === 404) {
return <NotFound />;
}
return <p>Something went wrong.</p>;
}isRouteErrorResponse narrows the error to a 4xx or 5xx response, so you can branch on the status. Data mode and its error boundaries are covered in React Router loaders and actions explained.
A loader can also throw a 404 response directly, which lands in the same ErrorBoundary with a status you can check.
Common mistakes
- Placing the catch-all before other routes, so it shadows them.
- Forgetting a way back home, leaving users stuck on the error page.
- Using path="/" as a catch-all, which only matches the root.
Rune AI
Key Insights
- Add a catch-all route with path * as the last route.
- Render a NotFound component with a link back home.
- Keep the catch-all last so it does not shadow real routes.
- Use useRouteError and isRouteErrorResponse in data mode.
Frequently Asked Questions
Where does the catch-all route go?
Does the 404 page need a server?
Can I show the requested path?
Conclusion
Build a 404 page with a catch-all route whose path is *, placed last in Routes. Render a not found component with a link back home. In data mode, use a route ErrorBoundary with useRouteError and isRouteErrorResponse.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.