Paramclasses Academy - Blog

Tutorials Details

←
Post 16 of 29
→

What is React Router — Building Multi-Page Applications and Navigation ?

What is React Router — Building Multi-Page Applications and Navigation ?

October 29, 2026

1. Introduction

React applications commonly use a single-page application (SPA) architecture. In an SPA, React can update the displayed content as users navigate without requiring a complete browser-page reload for every route change.

For example, an e-commerce website may contain these pages:

  • Home

  • Products

  • Product Details

  • About

  • Contact

  • Not Found

React Router helps connect URL paths to React components, allowing users to navigate between these pages.

In this tutorial, we will install React Router, configure routes, create navigation links, pass URL parameters, navigate programmatically, and implement a custom 404 page.

2. Learning Objectives

After completing this tutorial, you will be able to:

  1. Understand routing in React applications.

  2. Install and configure React Router.

  3. Create multiple pages using route definitions.

  4. Navigate using Link and NavLink.

  5. Access dynamic URL parameters using useParams.

  6. Navigate programmatically using useNavigate.

  7. Create nested routes and layouts.

  8. Display a custom 404 page.

  9. Organize a multi-page React application professionally.

3. Core Concepts and Explanation

3.1 What Is Routing?

Routing determines which page or component should be displayed for a particular URL.

For example:

URL path Component
/ Home
/about About
/products Products
/products/1 Product Details
/contact Contact
Any unmatched path Not Found

Without a routing library, you would need to implement URL matching and navigation behavior yourself.

React Router provides tools for handling these tasks.

3.2 Install React Router

Create a React project if you do not already have one:

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

Install React Router:

npm install react-router

Start the development server:

npm run dev

This tutorial uses the current declarative API from the react-router package. Some older tutorials use react-router-dom; follow the package and import conventions documented for the version installed in your project.

3.3 Understanding BrowserRouter, Routes, and Route

These are fundamental routing components.

  • BrowserRouter enables browser-based routing.

  • Routes selects the best matching route.

  • Route maps a URL pattern to a React element.

Example:

import {
  BrowserRouter,
  Routes,
  Route
} from "react-router";

function Home() {
  return <h1>Home Page</h1>;
}

function About() {
  return <h1>About Page</h1>;
}

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  );
}

export default App;

When the URL is /, React renders Home. When the URL is /about, React renders About.

The browser's address bar changes as navigation occurs.

3.4 Understanding Link

Use Link to navigate between routes without triggering a full document navigation.

import { Link } from "react-router";

function Home() {
  return (
    <div>
      <h1>Home Page</h1>
      <Link to="/about">Visit About Page</Link>
    </div>
  );
}

Using a regular anchor tag such as <a href="/about"> generally causes a document navigation. React Router's Link provides client-side navigation for application routes.

3.5 Understanding NavLink

NavLink is useful when navigation needs an active state, such as highlighting the current page in a menu.

import { NavLink } from "react-router";

function Navbar() {
  return (
    <nav>
      <NavLink
        to="/"
        end
        className={({ isActive }) =>
          isActive ? "active" : ""
        }
      >
        Home
      </NavLink>

      <NavLink
        to="/about"
        className={({ isActive }) =>
          isActive ? "active" : ""
        }
      >
        About
      </NavLink>
    </nav>
  );
}

The isActive value tells you whether a link matches the current route. The end prop makes the home link match only the root route rather than matching every URL that begins with /.

3.6 Understanding Dynamic Routes and useParams

Sometimes the same component needs to display different data depending on a URL parameter.

For example:

/products/1
/products/2
/products/3

Define a dynamic route:

<Route
  path="/products/:productId"
  element={<ProductDetails />}
/>

Read the parameter using useParams:

import { useParams } from "react-router";

function ProductDetails() {
  const { productId } = useParams();

  return <h1>Product ID: {productId}</h1>;
}

The :productId portion is a route parameter. It is available as a string, so convert it to a number if your application requires numeric calculations or comparisons.

3.7 Understanding useNavigate

useNavigate allows navigation in response to application logic, such as completing a form submission.

import { useNavigate } from "react-router";

function Login() {
  const navigate = useNavigate();

  function handleLogin() {
    // Perform actual authentication here.
    navigate("/dashboard");
  }

  return (
    <button onClick={handleLogin}>
      Continue to Dashboard
    </button>
  );
}

This example demonstrates navigation only. In a real application, do not treat a button click as proof that a user is authenticated. Navigate after the server has confirmed successful authentication.

3.8 Understanding Nested Routes and Outlet

Nested routes allow multiple pages to share a common layout, such as a navbar or sidebar.

The Outlet component marks where the selected child route should render.

import { Outlet } from "react-router";

function Layout() {
  return (
    <div>
      <header>
        <h1>My Application</h1>
      </header>

      <main>
        <Outlet />
      </main>
    </div>
  );
}

The parent layout remains in place while the matched child route changes.

4. Practical Code Examples

Example 1: Build a Complete Multi-Page Application

Create the following structure inside src:

src/
├── pages/
│   ├── Home.jsx
│   ├── About.jsx
│   ├── Products.jsx
│   ├── ProductDetails.jsx
│   ├── Contact.jsx
│   └── NotFound.jsx
├── App.jsx
├── main.jsx
└── index.css

Create src/pages/Home.jsx:

function Home() {
  return (
    <section>
      <h1>Welcome to Our Website</h1>
      <p>Learn React Router step by step.</p>
    </section>
  );
}

export default Home;

Create src/pages/About.jsx:

function About() {
  return (
    <section>
      <h1>About Us</h1>
      <p>We build modern web applications.</p>
    </section>
  );
}

export default About;

Create src/pages/Products.jsx:

import { Link } from "react-router";

const products = [
  { id: 1, name: "Laptop", price: 65000 },
  { id: 2, name: "Headphones", price: 2500 },
  { id: 3, name: "Keyboard", price: 1500 }
];

function Products() {
  return (
    <section>
      <h1>Products</h1>

      {products.map(product => (
        <article key={product.id}>
          <h2>{product.name}</h2>
          <p>Price: ₹{product.price}</p>

          <Link to={`/products/${product.id}`}>
            View Details
          </Link>
        </article>
      ))}
    </section>
  );
}

export default Products;

Create src/pages/ProductDetails.jsx:

import { Link, useParams } from "react-router";

const products = [
  { id: 1, name: "Laptop", price: 65000 },
  { id: 2, name: "Headphones", price: 2500 },
  { id: 3, name: "Keyboard", price: 1500 }
];

function ProductDetails() {
  const { productId } = useParams();

  const product = products.find(
    item => item.id === Number(productId)
  );

  if (!product) {
    return (
      <section>
        <h1>Product Not Found</h1>
        <Link to="/products">Back to Products</Link>
      </section>
    );
  }

  return (
    <section>
      <h1>{product.name}</h1>
      <p>Price: ₹{product.price}</p>

      <Link to="/products">Back to Products</Link>
    </section>
  );
}

export default ProductDetails;

Create src/pages/Contact.jsx:

function Contact() {
  return (
    <section>
      <h1>Contact Us</h1>
      <p>Email: contact@example.com</p>
    </section>
  );
}

export default Contact;

Create src/pages/NotFound.jsx:

import { Link } from "react-router";

function NotFound() {
  return (
    <section>
      <h1>404 - Page Not Found</h1>
      <p>The page you requested does not exist.</p>
      <Link to="/">Return Home</Link>
    </section>
  );
}

export default NotFound;

Now configure the routes in src/App.jsx:

import {
  BrowserRouter,
  Routes,
  Route,
  Link,
  NavLink
} from "react-router";

import Home from "./pages/Home";
import About from "./pages/About";
import Products from "./pages/Products";
import ProductDetails from "./pages/ProductDetails";
import Contact from "./pages/Contact";
import NotFound from "./pages/NotFound";

function Navbar() {
  return (
    <nav className="navbar">
      <Link className="brand" to="/">
        Param Store
      </Link>

      <div className="nav-links">
        <NavLink to="/" end>
          Home
        </NavLink>

        <NavLink to="/about">
          About
        </NavLink>

        <NavLink to="/products">
          Products
        </NavLink>

        <NavLink to="/contact">
          Contact
        </NavLink>
      </div>
    </nav>
  );
}

function App() {
  return (
    <BrowserRouter>
      <Navbar />

      <main className="container">
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/products" element={<Products />} />

          <Route
            path="/products/:productId"
            element={<ProductDetails />}
          />

          <Route path="/contact" element={<Contact />} />
          <Route path="*" element={<NotFound />} />
        </Routes>
      </main>
    </BrowserRouter>
  );
}

export default App;

Finally, add basic styling in src/index.css:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: Arial, sans-serif;
  background: #f4f7fb;
  color: #172033;
}

.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 16px;
  padding: 18px 6%;
  background: #111827;
}

.brand {
  color: white;
  font-size: 22px;
  font-weight: bold;
  text-decoration: none;
}

.nav-links {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
}

.nav-links a {
  color: #d1d5db;
  text-decoration: none;
}

.nav-links a.active {
  color: #22d3ee;
  font-weight: bold;
}

.container {
  max-width: 1000px;
  margin: 35px auto;
  padding: 0 20px;
}

article {
  margin: 16px 0;
  padding: 20px;
  background: white;
  border-radius: 12px;
  box-shadow: 0 4px 15px rgb(0 0 0 / 6%);
}

article a {
  color: #2563eb;
}

Ensure src/main.jsx imports the CSS and renders the application:

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import "./index.css";

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <App />
  </StrictMode>
);

Test your application:

  • Visit / for the Home page.

  • Visit /about for the About page.

  • Visit /products for the product list.

  • Open /products/1 for Laptop details.

  • Open /products/2 for Headphones details.

  • Open /products/99 for the Product Not Found message.

  • Visit /contact for the Contact page.

  • Visit /anything-else for the 404 page.

Example 2: Programmatic Navigation

Here is a small example of navigating after an action:

import { useNavigate } from "react-router";

function CheckoutSuccess() {
  const navigate = useNavigate();

  return (
    <div>
      <h2>Order completed</h2>

      <button onClick={() => navigate("/")}>
        Back to Home
      </button>
    </div>
  );
}

export default CheckoutSuccess;

In a real checkout flow, show the success page only after your backend confirms the order.

Example 3: Nested Routes

You can configure a shared layout with child routes:

import {
  BrowserRouter,
  Routes,
  Route,
  Outlet,
  Link
} from "react-router";

function DashboardLayout() {
  return (
    <div>
      <h1>Admin Dashboard</h1>

      <nav>
        <Link to="/dashboard">Overview</Link>{" "}
        <Link to="/dashboard/reports">Reports</Link>
      </nav>

      <Outlet />
    </div>
  );
}

function Overview() {
  return <h2>Dashboard Overview</h2>;
}

function Reports() {
  return <h2>Reports Page</h2>;
}

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route
          path="/dashboard"
          element={<DashboardLayout />}
        >
          <Route index element={<Overview />} />
          <Route path="reports" element={<Reports />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

export default App;

Here, /dashboard renders the overview inside the layout, while /dashboard/reports renders the reports page in the same layout.

5. Real-World Applications

E-commerce Website

  • /products displays the product catalog.

  • /products/:productId displays a particular product.

  • /cart displays the shopping cart.

  • /checkout displays the checkout flow.

Admin Dashboard

  • /dashboard displays an overview.

  • /dashboard/users displays user management.

  • /dashboard/reports displays reports.

Learning Management System

  • /courses displays available courses.

  • /courses/:courseId displays course details.

  • /lessons/:lessonId displays a specific lesson.

Portfolio Website

  • / displays the homepage.

  • /projects displays projects.

  • /about displays professional information.

  • /contact displays contact information.

For production deployments, configure the web server to serve the SPA entry point for valid application routes. Otherwise, direct navigation or refreshing a nested URL may return a server-side 404 even when the React route is valid.

6. Common Mistakes and Best Practices

Mistake 1: Forgetting to Install React Router

If the package is missing, imports will fail.

Solution:

npm install react-router

Mistake 2: Using the Wrong Import Convention

Use imports consistent with the React Router version and package installed in your project. The examples in this tutorial use react-router.

Mistake 3: Using a Regular Anchor for Internal Navigation

A normal <a href="/about"> can trigger a full document navigation.

Best practice: Use Link or NavLink for internal React Router navigation.

Mistake 4: Forgetting the Dynamic Route Parameter

Incorrect:

<Route path="/products" element={<ProductDetails />} />

Correct:

<Route
  path="/products/:productId"
  element={<ProductDetails />}
/>

Mistake 5: Ignoring Unknown Routes

Without a catch-all route, users may see an unhelpful blank page for an unmatched URL.

Best practice:

<Route path="*" element={<NotFound />} />

Mistake 6: Assuming Frontend Routes Provide Security

A route is not a security boundary. Hiding a dashboard link or redirecting a user does not protect private backend data.

Best practice: Enforce authentication and authorization on the server, and use frontend route checks only as part of the user experience.

Best-Practice Checklist

  • Use clear, meaningful route paths.

  • Use Link and NavLink for internal navigation.

  • Use dynamic parameters for resource-specific pages.

  • Handle invalid IDs and unknown routes.

  • Reuse shared layouts with nested routes.

  • Keep page components organized in separate files.

  • Configure SPA fallback behavior on the production server.

  • Protect sensitive data with server-side authorization.

7. Practice Exercises

Exercise 1: Create Pages

Create Home, About, Services, and Contact pages using React Router.

Exercise 2: Active Navigation

Use NavLink to highlight the currently selected page.

Exercise 3: Dynamic Product Details

Create a route /products/:productId and display the matching product.

Exercise 4: Custom 404 Page

Display a custom Not Found page for invalid URLs.

Exercise 5: Nested Dashboard

Create a dashboard layout with Overview and Reports child routes using Outlet.

Exercise 6: Programmatic Navigation

After a simulated successful form submission, navigate to a confirmation page using useNavigate.

8. Key Takeaways and Conclusion

In this tutorial, you learned:

  • React Router maps URL paths to React components.

  • BrowserRouter, Routes, and Route configure routing.

  • Link enables client-side navigation.

  • NavLink can indicate the active route.

  • useParams reads dynamic URL parameters.

  • useNavigate supports programmatic navigation.

  • Outlet renders nested route content.

  • Catch-all routes provide a custom 404 page.

  • Production servers must support direct requests to SPA routes.

  • Real security must be enforced by the backend.

Final takeaway: React Router gives your React application a structured navigation system, making it easier to build multi-page user experiences without requiring a full browser-page reload for every internal navigation.

←
Post 16 of 29
→