React Handling 404 Pages

In the last lesson, React useNavigate Hook, you learned to navigate from code. This lesson adds a 404 page, a friendly “page not found” that catches any URL your routes don’t match.

🤔 Why do we need a 404 page?

Here’s the pain. URLs that don’t match any route are common, and a blank screen for them is confusing.

  • A user mistypes a URL, like /abuot instead of /about.
  • An old link, from a bookmark or an email, points to a page you’ve removed.
  • Without a catch-all, React Router matches nothing and renders an empty screen.
  • The user thinks the whole app is broken, when really it’s just a wrong URL.

So a 404 page is about handling the unknown gracefully. Instead of a blank screen, the user sees a clear “this page doesn’t exist” with a way back home.

🧩 The catch-all route with path=”*”

React Router has a special path, * (a star), that matches any URL. You add it as the last route, so it only catches what nothing else matched.

import { Routes, Route } from "react-router-dom";
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
{/* catch-all: matches any URL the routes above didn't */}
<Route path="*" element={<NotFound />} />
</Routes>
);
}

Read how the catch-all fits in.

  • path="*" matches any URL at all.
  • It’s listed last, so React Router tries the real routes first.
  • If the URL is / or /about, those match and the star is never reached.
  • If the URL is anything else, like /xyz, only the star matches, so NotFound shows.

Put the catch-all last

The * route must be the last one. React Router checks routes in order and the star matches everything, so if you put it first, it would swallow every URL and your real pages would never show.

💡 Building the Not Found page

The NotFound component is just a normal component. Make it friendly and give the user a way back.

import { Link } from "react-router-dom";
function NotFound() {
return (
<div>
<h1>404 - Page Not Found</h1>
<p>Sorry, the page you are looking for does not exist.</p>
<Link to="/">Go back home</Link>
</div>
);
}

See what makes a good 404 page.

  • A clear heading so the user instantly understands what happened.
  • A short, calm message, not a scary error.
  • A Link back to the home page, so the user isn’t stuck.
  • It’s a plain component, so you can style it however you like.

Output

(visit /some-wrong-url )
404 - Page Not Found
Sorry, the page you are looking for does not exist.
Go back home

🔍 Why this is better than a blank screen

It’s worth being clear on what the catch-all actually buys you.

  • Without it, an unknown URL renders nothing, which looks like the app crashed.
  • With it, the user gets a clear message that the page doesn’t exist.
  • The Link home turns a dead end into an easy recovery.
  • It also looks professional, the same as 404 pages on big websites.

So the catch-all route is a small addition that makes your app feel finished and trustworthy, instead of broken on a wrong URL.

⚠️ Common Mistakes

The most common mistake is putting the catch-all route too early, so it grabs every URL.

// ❌ star route first - it matches everything, so /about never shows
<Routes>
<Route path="*" element={<NotFound />} />
<Route path="/about" element={<About />} />
</Routes>
// ✅ star route last - real routes match first, star catches the rest
<Routes>
<Route path="/about" element={<About />} />
<Route path="*" element={<NotFound />} />
</Routes>

Keep these in mind.

  • Don’t put path="*" before your real routes. It must be last.
  • Don’t leave out a catch-all entirely, or unknown URLs show a blank screen.
  • Don’t make the 404 page a dead end. Always include a link back home.

✅ Best Practices

A few habits for handling unknown URLs.

  • Always add a path="*" catch-all route, and put it last in your Routes.
  • Keep the message friendly and short, not a technical error.
  • Give the user a clear way out, like a link to the home page.
  • Match the 404 page’s style to the rest of your app, so it feels part of it.

One catch-all is enough

You only need one path="*" route for the whole app. It sits at the bottom of your main Routes and quietly handles every URL that none of your real routes matched.

🧩 What You’ve Learned

  • ✅ URLs that match no route would otherwise show a blank, broken-looking screen
  • ✅ A catch-all route with path="*" matches any URL your other routes didn’t
  • ✅ Put the * route last, so real routes are tried first
  • ✅ Show a friendly Not Found page with a clear message
  • ✅ Always include a Link back home so the user isn’t stuck
  • ✅ One catch-all at the bottom of your Routes handles every unknown URL

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 1

    What path matches any URL for a 404 catch-all route?

    Why: The star path, path='*', matches any URL. You use it as the catch-all to show a Not Found page for URLs no other route matched.

  2. 2

    Where should the catch-all route go in your Routes?

    Why: React Router checks routes in order and the star matches everything. So it must be last, or it would swallow every URL before your real routes are tried.

  3. 3

    What happens without a catch-all route when a user visits an unknown URL?

    Why: With no matching route and no catch-all, React Router renders nothing in that spot, so the user sees a confusing blank screen.

  4. 4

    What should a good 404 page include?

    Why: A friendly message explains what happened, and a link home gives the user an easy way out instead of a dead end.

🚀 What’s Next?

That wraps up the React Router module. Your app now has real pages, dynamic routes, navigation, and a proper 404 page. Next we go back to managing data, but app-wide, with the Context API, so many components can share state without prop drilling.

React Introduction to Context API

Share & Connect