Share
1

React Router: Navigating Your Single-Page Applications

by ObserverPoint · June 30, 2025

You’ve built your React components, managed their state with Hooks, and styled them beautifully. Now, how do you manage multiple “pages” or views within your single-page application (SPA) without full page reloads? This is where client-side routing comes into play, and React Router is the de-facto standard library for achieving this in React. It allows you to create dynamic navigation experiences, mapping different URLs to different components, giving your users the feel of a multi-page website within a single, fast-loading application.

Understanding React Router is crucial for building any non-trivial React application. It seamlessly integrates with your component structure, enabling you to define navigation paths, pass data through URLs, and render specific components based on the current route. This article will guide you through the core components of React Router, including `BrowserRouter`, `Routes`, `Route`, `Link`, and `NavLink`, and explain how to implement nested routes and handle route parameters.

Setting Up Client-Side Routing

Traditional websites rely on the server to send a new HTML page for every URL visited. In a Single-Page Application (SPA) built with React, the entire application loads once, and subsequent “page” changes are handled by JavaScript in the browser. This means that when a user clicks a link, instead of requesting a new HTML document from the server, React Router intercepts the navigation, updates the URL in the browser’s history, and renders the appropriate React component.

The first step is to install React Router in your project:

npm install react-router-dom
    

Or with Yarn:

yarn add react-router-dom
    

The `react-router-dom` package provides the DOM-specific bindings for React Router, which is what you’ll typically use for web applications.

Once installed, you’ll typically wrap your entire application (or the part of your application that needs routing) with a router component. The most common one for web applications is `BrowserRouter`.

// src/index.js or App.js
import React from 'react';
import ReactDOM from 'react-dom/client'; // For React 18+
import { BrowserRouter } from 'react-router-dom';
import App from './App'; // Your main App component

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <React.StrictMode>
        <BrowserRouter>
            <App />
        </BrowserRouter>
    </React.StrictMode>
);
    

The `BrowserRouter` uses the HTML5 history API (`pushState`, `replaceState`, `popState`) to keep your UI in sync with the URL. This is critical for creating a seamless user experience that still respects the browser’s back/forward buttons and direct URL access.

`BrowserRouter`, `Routes`, `Route`, `Link`, `NavLink`

These are the core building blocks you’ll use to define and navigate your routes.

`Routes` and `Route`

The `<Routes>` component is a container for `<Route>` components. It looks at the current URL and renders the first `<Route>` that matches the URL path. This “first match wins” behavior makes route definition predictable.

The `<Route>` component is responsible for mapping a URL path to a specific React component. It takes a `path` prop (the URL path) and an `element` prop (the React component to render when the path matches).

// src/App.js
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import HomePage from './pages/HomePage';
import AboutPage from './pages/AboutPage';
import ContactPage from './pages/ContactPage';
import NotFoundPage from './pages/NotFoundPage'; // For unmatched routes

function App() {
    return (
        <div>
            <nav>
                {/* Navigation links will go here */}
            </nav>

            <Routes>
                <Route path="/" element={<HomePage />} />
                <Route path="/about" element={<AboutPage />} />
                <Route path="/contact" element={<ContactPage />} />
                <Route path="*" element={<NotFoundPage />} /> {/* Catch-all route */}
            </Routes>
        </div>
    );
}
    

The `path=”*”` is a wildcard route that matches any path not matched by previous routes, making it ideal for a 404 “Not Found” page.

`Link` and `NavLink`

To navigate between routes, you should use React Router’s `<Link>` or `<NavLink>` components instead of standard `<a>` tags. Using `<a>` tags would trigger a full page reload, defeating the purpose of an SPA. `<Link>` prevents the default browser behavior and handles the navigation client-side.

The `<Link>` component takes a `to` prop, which specifies the path to navigate to.

import { Link } from 'react-router-dom';

function Navigation() {
    return (
        <nav>
            <ul>
                <li>
                    <Link to="/">Home</Link>
                </li>
                <li>
                    <Link to="/about">About</Link>
                </li>
                <li>
                    <Link to="/contact">Contact</Link>
                </li>
            </ul>
        </nav>
    );
}
    

`<NavLink>` is a special version of `<Link>` that automatically adds styling attributes (`activeClassName` or `className` with a function, `activeStyle`) to the rendered element when it matches the current URL. This is extremely useful for highlighting the currently active navigation item, a common UI pattern.

import { NavLink } from 'react-router-dom';

function ActiveNavigation() {
    return (
        <nav>
            <ul>
                <li>
                    <NavLink
                        to="/"
                        className={({ isActive }) => (isActive ? 'active-link' : undefined)}
                    >
                        Home
                    </NavLink>
                </li>
                <li>
                    <NavLink
                        to="/dashboard"
                        style={({ isActive }) => ({ color: isActive ? 'red' : 'blue' })}
                    >
                        Dashboard
                    </NavLink>
                </li>
            </ul>
        </nav>
    );
}
    

The `className` prop for `NavLink` in React Router v6+ accepts a function that receives an object with an `isActive` property, allowing for dynamic class assignment. Similarly for the `style` prop.

Nested Routes and Route Parameters

As your application grows, you’ll often need more complex routing structures. React Router supports two powerful features for this: nested routes and route parameters.

Nested Routes

Nested routes allow you to define routes that render components within the layout of a parent route. For example, a dashboard might have different sub-sections (e.g., `/dashboard/profile`, `/dashboard/settings`). Instead of defining completely separate routes for these, you can nest them. The parent route renders a wrapper component, and the child routes render their content inside that wrapper. The `<Outlet />` component from `react-router-dom` is used in the parent component to indicate where the nested child routes should be rendered.

// src/pages/DashboardPage.js
import React from 'react';
import { Outlet, Link } from 'react-router-dom';

function DashboardPage() {
    return (
        <div>
            <h2>Dashboard</h2>
            <nav>
                <ul>
                    <li><Link to="profile">Profile</Link></li>
                    <li><Link to="settings">Settings</Link></li>
                </ul>
            </nav>

            {/* <Outlet /> renders the matched child route's element here */}
            <hr />
            <Outlet />
        </div>
    );
}

export default DashboardPage;

// --- In your App.js (or wherever your main <Routes> are) ---
// import DashboardPage from './pages/DashboardPage';
// import DashboardProfile from './components/DashboardProfile';
// import DashboardSettings from './components/DashboardSettings';

<Routes>
    <Route path="/" element={<HomePage />} />
    <Route path="/dashboard" element={<DashboardPage />}>
        {/* Child routes of /dashboard */}
        <Route path="profile" element={<DashboardProfile />} />
        <Route path="settings" element={<DashboardSettings />} />
        <Route index element={<h3>Welcome to your Dashboard!</h3>} /> {/* Default child route for /dashboard */}
    </Route>
    {/* ...other routes */}
</Routes>
    

The `index` prop on a `<Route>` indicates that this route should be rendered when the parent path is matched exactly (e.g., `/dashboard` without any sub-path).

Route Parameters

Route parameters allow you to capture dynamic values from the URL. For example, you might have a product details page where the product ID is part of the URL (`/products/123`). You define parameters in your `<Route>` path using a colon (`:`).

To access these parameters in your component, you use the `useParams` Hook provided by `react-router-dom`.

// src/pages/ProductDetailsPage.js
import React from 'react';
import { useParams } from 'react-router-dom';

function ProductDetailsPage() {
    const { productId } = useParams(); // Get the 'productId' from the URL

    // In a real app, you'd use productId to fetch data for this specific product
    // const [product, setProduct] = useState(null);
    // useEffect(() => {
    //   fetchProductById(productId).then(data => setProduct(data));
    // }, [productId]);

    return (
        <div>
            <h2>Product Details</h2>
            <p>Displaying details for Product ID: {productId}</p>
        </div>
    );
}

export default ProductDetailsPage;

// --- In your App.js (or wherever your main <Routes> are) ---
// import ProductDetailsPage from './pages/ProductDetailsPage';

<Routes>
    {/* ...other routes */}
    <Route path="/products/:productId" element={<ProductDetailsPage />} />
</Routes>

// --- Example Link to this route ---
// <Link to="/products/456">View Product 456</Link>
    

Route parameters are essential for building dynamic content pages where the content displayed depends on an ID or slug in the URL. They are a core part of creating powerful and flexible routing structures in your React applications. Mastering React Router will allow you to build sophisticated user experiences, guiding users through different sections of your SPA seamlessly and intuitively.

References

You may also like