Scientyfic World

React Router Tutorial for Beginners (v8): Routes, Params & Layouts

React Router is the most widely used routing library for React. It maps URLs to components, so a single-page React app can have multiple pages (/, /about, /users/42) with working...

Share:

Get an AI summary of this article

react router

React Router is the most widely used routing library for React. It maps URLs to components, so a single-page React app can have multiple pages (/, /about, /users/42) with working links, the back button and shareable URLs, all without full page reloads. The current version is React Router 8, installed as the react-router package.

This is a hands-on beginner tutorial. You will build a small app with a shared layout, active navigation links, dynamic URL parameters, a redirect, a 404 page and a protected route. I built it with React Router 8.5, React 19.3 and Vite 8.3 and clicked through every route in a browser to confirm the behaviour described here. If you already know the basics and want the bigger picture (framework mode, loaders and actions, server rendering), read my React Router v7 explainer, most of which still applies to v8.

What Is Routing in a React App?

On a traditional website, every link asks the server for a new HTML page. A React single-page application (SPA) loads one HTML page and then swaps components in and out with JavaScript. Without a router, the URL never changes, so users can’t bookmark a page, share a link or use the back button.

Client-side routing fixes that. When you click a link, React Router updates the URL with the browser’s History API and renders the component that matches it, without asking the server for a new page. The result feels instant and still behaves like a normal website.

React Router 8: What Changed?

React Router 8 was released in June 2026 and is deliberately a small upgrade from v7. The official upgrade guide lists the main changes:

  • One package. react-router-dom no longer exists in v8. Import everything from react-router (and DOM-specific pieces such as RouterProvider from react-router/dom). Many older tutorials still use react-router-dom; that is the v6/v7 name.
  • Newer requirements: React 19.2.7 or later and Node.js 22.22 or later, and the package is ESM-only.
  • Former v7 “future flags” are now the default, such as middleware in framework mode.

React Router v6 is now end-of-life. The components used in this tutorial (BrowserRouter, Routes, Route, Link, NavLink, Outlet and the hooks) work the same way in v7 and v8.

What Happened to react-router-dom?

For years, the package you installed for web apps was react-router-dom, and most tutorials, Stack Overflow answers and older code still import from it. Here is how it changed:

VersionWhat to installWhere to import from
v5 and v6react-router-domreact-router-dom (it re-exported the core react-router)
v7react-routerreact-router; react-router-dom still worked as a re-export to ease upgrades
v8react-routerreact-router, plus react-router/dom for RouterProvider. The react-router-dom package was removed

So if you search for “react router dom” today, the current answer is: install react-router and change every from 'react-router-dom' import to from 'react-router'. The components and hooks have the same names.

Coming From React Router v5?

Many older guides, including the 2023 “React Router DOM” guide that used to live on this site, teach the v5 API. These are the replacements:

v5v6, v7 and v8
<Switch><Routes>
<Route path="/about" component={About} /><Route path="/about" element={<About />} />
exact propNot needed; routes match exactly by default (use /* to match deeper paths)
<Redirect to="/login" /><Navigate to="/login" replace />
useHistory() and history.push(path, state)useNavigate() and navigate(path, { state })
useRouteMatch()useMatch()
withRouter(Component)Hooks: useNavigate, useLocation, useParams
Nested <Switch> inside componentsNested <Route> elements with <Outlet /> in the parent

The official guides cover the move from v6 to v7 and from v7 to v8; for v5, upgrade to v6 first.

The Three Ways to Use React Router

ModeSet up withBest for
Declarative<BrowserRouter> and <Routes>Learning, and simple SPAs that fetch data in components
DatacreateBrowserRouter and <RouterProvider>SPAs that want loaders, actions and pending UI
FrameworkThe React Router Vite plugin and route modulesFull-stack apps with server rendering, type-safe routes and code splitting

This tutorial uses declarative mode because it is the simplest way to learn the concepts, and everything you learn carries over to the other two.

Step 1: Create a React App and Install React Router

npm create vite@latest router-demo -- --template react
cd router-demo
npm install
npm install react-router

Step 2: Wrap the App in BrowserRouter

BrowserRouter keeps the UI in sync with the address bar. Put it around your app once, in src/main.jsx:

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router';
import App from './App.jsx';

createRoot(document.getElementById('root')).render(
  <StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </StrictMode>,
);

Step 3: Define Your Routes

Routes are declared with <Routes> and nested <Route> elements. This is src/App.jsx for the whole tutorial:

import { Routes, Route, Navigate } from 'react-router';
import Layout from './Layout.jsx';
import { Home, About, Users, UserDetail, Login, Dashboard, NotFound } from './pages.jsx';
import RequireAuth from './RequireAuth.jsx';

export default function App() {
  return (
    <Routes>
      <Route element={<Layout />}>
        <Route index element={<Home />} />
        <Route path="about" element={<About />} />
        <Route path="users">
          <Route index element={<Users />} />
          <Route path=":userId" element={<UserDetail />} />
        </Route>
        <Route path="login" element={<Login />} />
        <Route
          path="dashboard"
          element={
            <RequireAuth>
              <Dashboard />
            </RequireAuth>
          }
        />
        <Route path="team" element={<Navigate to="/about" replace />} />
        <Route path="*" element={<NotFound />} />
      </Route>
    </Routes>
  );
}
  • path is the URL segment and element is what to render.
  • A route with index renders at its parent’s exact path: the home page at /, the user list at /users.
  • Child paths are relative to their parent, so ":userId" inside "users" matches /users/1.
  • path="*" matches anything that no other route matched: your 404 page.
  • <Navigate to="/about" replace /> redirects /team to /about.

Step 4: A Shared Layout with Outlet and NavLink

The first <Route> above has no path, only an element: it is a layout route. Every child route renders inside it, where the <Outlet /> is:

import { NavLink, Outlet } from 'react-router';

export default function Layout() {
  const style = ({ isActive }) => ({ fontWeight: isActive ? 'bold' : 'normal' });

  return (
    <>
      <nav>
        <NavLink to="/" end style={style}>Home</NavLink> |{' '}
        <NavLink to="/about" style={style}>About</NavLink> |{' '}
        <NavLink to="/users" style={style}>Users</NavLink> |{' '}
        <NavLink to="/dashboard" style={style}>Dashboard</NavLink>
      </nav>
      <main>
        <Outlet /> {/* the matched child route renders here */}
      </main>
    </>
  );
}

NavLink is a Link that knows whether it is active, so you can style the current page. The end prop on the Home link stops it from being active on every page, since every path starts with /. When I clicked through the app, the bold link always matched the current section: Users stayed bold on /users/1 as well, because that page is inside the users section.

Step 5: Pages, Links and URL Parameters

Here are the page components (src/pages.jsx). Use <Link> instead of <a> for internal links; an <a href> would reload the whole app.

import { Link, useParams, useNavigate, useLocation } from 'react-router';
import { useAuth } from './auth.js';

const USERS = [
  { id: '1', name: 'Ada Lovelace' },
  { id: '2', name: 'Alan Turing' },
];

export const Home = () => <h1>Home</h1>;
export const About = () => <h1>About</h1>;

export function Users() {
  return (
    <>
      <h1>Users</h1>
      <ul>
        {USERS.map((u) => (
          <li key={u.id}><Link to={u.id}>{u.name}</Link></li>
        ))}
      </ul>
    </>
  );
}

export function UserDetail() {
  const { userId } = useParams();               // "1" for /users/1
  const user = USERS.find((u) => u.id === userId);
  if (!user) return <h1>No user with id {userId}</h1>;
  return <h1>{user.name}</h1>;
}

export function Login() {
  const navigate = useNavigate();
  const location = useLocation();
  const { login } = useAuth();
  const from = location.state?.from ?? '/';     // where the user was going

  function handleLogin() {
    login();
    navigate(from, { replace: true });           // go back to the protected page
  }

  return <button onClick={handleLogin}>Log in</button>;
}

export const Dashboard = () => <h1>Dashboard (private)</h1>;

export const NotFound = () => (
  <>
    <h1>Page not found</h1>
    <Link to="/">Go home</Link>
  </>
);

useParams() returns the dynamic segments of the URL as strings. Visiting /users/2 rendered “Alan Turing”, and /users/99 rendered “No user with id 99”, so always handle the case where the parameter doesn’t match anything. For query strings like ?page=2&sort=new, use useSearchParams(); I cover it in detail in how to use query parameters with React Router.

Step 6: Navigate in Code with useNavigate

Links cover most navigation, but sometimes you need to move the user after an action, such as submitting a form or logging in. useNavigate() returns a function for that; the Login component above calls navigate(from, { replace: true }) after logging in. replace: true replaces the current history entry, so the back button doesn’t return to the login page. You can also call navigate(-1) to go back.

Step 7: Protect Private Routes

A common pattern is a wrapper component that checks whether the user is logged in and redirects to /login if not, remembering where they were going:

import { Navigate, useLocation } from 'react-router';
import { useAuth } from './auth.js';

export default function RequireAuth({ children }) {
  const { isLoggedIn } = useAuth();
  const location = useLocation();

  if (!isLoggedIn) {
    // remember where the user wanted to go, then send them to /login
    return <Navigate to="/login" replace state={{ from: location.pathname }} />;
  }
  return children;
}

For the demo, useAuth is a tiny in-memory store (src/auth.js); in a real app it would come from your auth provider, such as Firebase Authentication:

import { useSyncExternalStore } from 'react';

// Tiny in-memory auth store for the demo; use your real auth provider instead
let loggedIn = false;
const listeners = new Set();

export function useAuth() {
  const isLoggedIn = useSyncExternalStore(
    (cb) => (listeners.add(cb), () => listeners.delete(cb)),
    () => loggedIn,
  );
  return {
    isLoggedIn,
    login: () => { loggedIn = true; listeners.forEach((l) => l()); },
  };
}

In my test, clicking Dashboard while logged out landed on /login; clicking “Log in” went straight to /dashboard, showing “Dashboard (private)”. Remember that this only controls what the UI shows. Real security must be enforced by your API, because anyone can change client-side JavaScript.

Step 8: Deploying a React Router SPA

Navigating inside the app works anywhere, but if someone opens /users/2 directly or refreshes the page, the browser asks the server for that path. A plain static server returns 404 because there is no users/2 file. Configure your host to serve index.html for unknown paths so React Router can take over:

  • Netlify: a _redirects file containing /* /index.html 200.
  • Vercel: a rewrite of all paths to /index.html in vercel.json.
  • Nginx: try_files $uri /index.html;

I tested the deep links with a small static server that falls back to index.html, and /users/2, /users/99 and /does/not/exist all rendered the right page on a fresh load. If SEO and fast first loads matter, consider framework mode or server-side rendering so search engines receive real HTML for each URL.

React Router Hooks and Components Cheat Sheet

APIWhat it does
<BrowserRouter>Provides routing using the browser URL (declarative mode)
<Routes>, <Route>Declare which element renders for which path
<Link to>Client-side link with no page reload
<NavLink>Link that knows if it is active, for navigation menus
<Outlet />Where a layout renders its matched child route
<Navigate to>Redirect when rendered
useParams()Dynamic URL segments, such as :userId
useSearchParams()Read and update the query string
useNavigate()Navigate from code
useLocation()The current location: pathname, search, hash and state

Common Mistakes

  • Importing from react-router-dom in a v8 project. Use react-router.
  • Using <a href> for internal links, which reloads the whole app.
  • Forgetting <Outlet /> in a layout, so child routes never appear.
  • Forgetting end on a NavLink to /, so Home always looks active.
  • Not configuring the host’s fallback to index.html, so refreshes and shared links return 404.
  • Treating client-side route protection as security.

Conclusion

With a handful of pieces (BrowserRouter, Routes and Route, Link and NavLink, Outlet and three hooks) you can give any React app real pages, nested layouts, dynamic URLs, redirects and protected sections. Start in declarative mode as in this tutorial, and move to data or framework mode when you want React Router to handle data loading and server rendering for you.

FAQs

What is React Router used for?

It adds client-side routing to React apps: it maps URLs to components so a single-page app can have multiple pages with working links, browser history and shareable URLs, without full page reloads.

What is the latest version of React Router?

React Router 8, released in June 2026, installed with npm install react-router. It requires React 19.2 or later and removes the separate react-router-dom package.

What is the difference between react-router and react-router-dom?

In v6, react-router-dom contained the browser-specific APIs. In v7 it became a re-export of react-router, and in v8 it was removed. Use the react-router package for new projects.

How do I get URL parameters in React Router?

Define a dynamic segment such as path=”:userId”, then call useParams() in the component: const { userId } = useParams(). Values are always strings.

How do I redirect in React Router?

Render in your JSX, or call navigate(“/path”) from the useNavigate() hook after an event such as a form submission.

How do I make a 404 page with React Router?

Add a route with path=”*” as the last child route. It matches any URL that no other route matched.

Why do I get a 404 when I refresh a React Router page?

The server looks for a file at that path and doesn’t find one. Configure your host to serve index.html for unknown paths so React Router can handle the URL in the browser.

Snehasish Konger
Developed @scientyficworld.org | Technical writer @Nected | Content Developer
Connect with Snehasish Konger

On This page

Take a Pause with Intervals

A Sunday letter on building, writing, and thinking deeper as a developer — short, honest, and worth your time.

Snehasish Konger profile photo

"Hey there — I'm Snehasish. Hope this post saved you some head-scratching time! I've spent years turning technical chaos into clarity, and I'm here to be your guide through the maze of modern tech. Stick around for more lightbulb moments — we're just getting started."

Related Posts