Runnable lesson · press Try it on the examples to edit and run them here.
A website has many pages, and each page has its own address. A single-page app has one HTML file. Routing is how such an app still gives users different “pages” with different URLs, working Back and Forward buttons and shareable links.
What a router has to do
- Read the URL (
/products/42). - Choose which components to show for that URL.
- Change the URL without reloading when the user clicks a link (using the browser’s History API).
- React to Back and Forward buttons.
- Pass information from the URL (such as
42) to your components.
Without a router, a React app has one address for everything. Users could not bookmark a product, share a link, or press Back to return to the previous screen.
A toy router
At its core, routing is “choose a component by a value”. Here is the idea in a few lines, using state in place of the address bar (the playground cannot change its own URL):
function Home() { return <h2>Home page</h2>; }
function About() { return <h2>About page</h2>; }
function Products() { return <h2>Products page</h2>; }
const routes = {
"/": Home,
"/about": About,
"/products": Products,
};
function App() {
const [path, setPath] = React.useState("/");
const Page = routes[path] ?? (() => <h2>Not found</h2>);
return (
<div>
<nav style={{ display: "flex", gap: 12 }}>
<button onClick={() => setPath("/")}>Home</button>
<button onClick={() => setPath("/about")}>About</button>
<button onClick={() => setPath("/products")}>Products</button>
</nav>
<p>Current path: {path}</p>
<Page />
</div>
);
}
That is the entire idea. A real router adds everything this toy lacks: it reads and writes the browser’s address bar, supports Back and Forward, reads parameters (/products/:id), nests layouts, and works with real <a> links that people can open in a new tab.
Real links, not buttons
Navigation should use links (<a href>), because users expect to right-click, open in a new tab, copy the address and have search engines and assistive technology understand them. A router gives you a Link component that renders a real <a>, but handles the click without a page reload.
The History API
Routers use two browser features:
history.pushState(...)changes the address bar and adds a history entry without loading a page.- The
popstateevent fires when the user presses Back or Forward, so the app can show the matching screen.
history.pushState({}, "", "/about"); // address bar now says /about; no reload
window.addEventListener("popstate", () => {
// read location.pathname and update the screen
});
Writing this by hand for every feature (parameters, nested layouts, redirects, scroll restoration) is a lot of work, which is why almost every app uses a router library.
The server must cooperate
Because the app has one HTML file, a direct visit or a refresh on /products/42 asks the server for a file that does not exist. The server must return index.html for every path your router handles, and let the app take over. You will configure that in Deployment Fundamentals. Without it you see a 404 on refresh, one of the most common beginner problems.
Which router?
The React documentation recommends building new apps with a framework, and many frameworks include routing: Next.js, React Router in framework mode and others. For a client-side app built with Vite, the standard library is React Router. You will use it in the next lessons.
Key points
- Routing maps a URL to the components that should be shown, in a single-page app.
- A router uses the History API so navigation does not reload the page.
- Use real links, and make the server return
index.htmlfor app routes.