React Router Tutorial: Routes, Links, and Layouts

Set up client-side routing in React with React Router. Map URL paths to components with Routes and Route, link between pages with Link, and share a layout with Outlet.

6 min read

React Router is a client-side routing library for React. It maps URL paths to components so moving between pages changes the view without a full browser reload. This tutorial covers the three pieces every React Router app starts with: routes, links, and layouts.

The examples use React Router 8 and its declarative BrowserRouter mode. This is the simplest way to add routing to a standard React project, and every API shown here imports from one package. You can keep the same package as the app grows, since React Router also ships a data mode and a framework mode for server rendering.

Install and mount the router

React Router ships as a single npm package called react-router. Install it, then wrap your app in a BrowserRouter so the router can read the current URL and re-render on navigation.

bashbash
npm install react-router
One package since v7

Older tutorials import from react-router-dom. React Router 7 merged that package into react-router, so current code imports everything from react-router.

BrowserRouter watches the browser History API and keeps the URL bar in sync with the UI. Mount it once around your root component in main.jsx. The router holds the current location in context, so any component below it can read the URL.

App.jsxApp.jsx
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router";
import App from "./App.jsx";
createRoot(document.getElementById("root")).render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

Everything inside BrowserRouter can now read the URL and render route matches. A full walkthrough, including the create command, is in how to add React Router to a Vite project.

Map paths to components with Routes

Routes is where URLs meet components. The Routes component holds Route children, and each Route maps one path to one element.

App.jsxApp.jsx
import { Routes, Route } from "react-router";
import Home from "./pages/Home.jsx";
import About from "./pages/About.jsx";
export default function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/about" element={<About />} />
    </Routes>
  );
}

When the URL is /, React Router renders Home. When it is /about, it renders About. Visiting any other path renders nothing, because no route matches.

React Router ranks route paths, so a more specific path wins even when it is listed later. Each element is a normal React component, so pages can hold any state and markup you already write.

That empty result for unknown paths is why most apps add a catch-all route later. The 404 pattern is covered in how to build a 404 page with React Router.

Links between pages use the Link component instead of raw anchor tags. Link renders an anchor element but intercepts the click so React Router can swap components without reloading the page.

App.jsxApp.jsx
import { Link } from "react-router";
 
export default function Nav() {
  return (
    <nav>
      <Link to="/">Home</Link>
      <Link to="/about">About</Link>
    </nav>
  );
}

Clicking About updates the URL to /about and renders the About route, with no network request for a new HTML document. Link also lets users open a page in a new tab or copy the address, because it renders a real anchor element with an href.

NavLink works the same way but adds an active class to the link that matches the current URL.

App.jsxApp.jsx
import { NavLink } from "react-router";
 
export default function Nav() {
  return (
    <nav>
      <NavLink to="/" end>Home</NavLink>
      <NavLink to="/about">About</NavLink>
    </nav>
  );
}

The end prop on the first link stops it from matching every path. Without it, the / link would also look active on /about. Use NavLink where an active state helps, like a navigation bar or a set of tabs.

Share a layout with nested routes

A layout is a page frame shared by several routes, like a header and a content area. Instead of repeating the header in every page, nest the routes under one parent and render the active child with Outlet.

The Layout component is the frame. It renders the shared header once and leaves an Outlet where the active page should appear.

App.jsxApp.jsx
import { Outlet } from "react-router";
 
function Layout() {
  return (
    <div>
      <header>My site</header>
      <Outlet />
    </div>
  );
}

The App component then nests the two pages under this layout route. The parent Route has no path, so it adds the frame without changing the URL.

App.jsxApp.jsx
import { Routes, Route } from "react-router";
import Home from "./pages/Home.jsx";
import About from "./pages/About.jsx";
export default function App() {
  return (
    <Routes>
      <Route element={<Layout />}>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Route>
    </Routes>
  );
}

When /about matches, React Router renders About into the Outlet inside Layout, so the header stays put while the content changes.

A parent without a path is only one nesting option. Parent routes can also own a URL prefix and render deeper children, which how to create nested routes with React Router explains in depth.

When React Router helps

A single page app can fake navigation with state, but the URL never changes. React Router keeps the address bar honest, so refresh, bookmarks, share links, and the back button all work the way users expect.

What to learn next

This declarative setup is the smallest complete React Router app. From here, add URL parameters for product and post pages, or explore how the declarative, data, and framework modes differ when an app grows beyond client-side rendering. Buttons that navigate after an action use the useNavigate hook.

Common early mistakes are importing from the old react-router-dom package, forgetting to wrap the app in BrowserRouter, and nesting routes without an Outlet in the parent.

Rune AI

Rune AI

Key Insights

  • Install the react-router package and wrap the app in BrowserRouter.
  • Declare one Route per URL path inside Routes.
  • Use Link for navigation and NavLink for active styling.
  • Nest routes and render the child with Outlet.
RunePowered by Rune AI

Frequently Asked Questions

Do I need a server to use React Router?

No. In declarative mode React Router runs fully in the browser. It updates the URL with the History API and renders the matching component without a page reload.

What package do I install?

Install the react-router package. React Router 7 merged the old react-router-dom package into it, so you import everything from react-router.

Is React Router a framework or a library?

It can be both. You can use it as a small routing library with BrowserRouter and Routes, or adopt its data and framework modes for loaders, actions, and server rendering.

Conclusion

React Router maps URL paths to components in a client-side React app. Configure routes with Routes and Route, navigate with Link, and share a page frame with nested routes and Outlet. Add a Vite setup and deeper routing guides when you are ready to build further.