Mastering React Router: Building Dynamic Navigation in React Applications

Understanding React Router's Purpose

Imagine you're designing a city's transportation system. Just as a city needs clear routes and navigation to help people reach their destinations, a React application needs a routing system to help users navigate between different views and components. React Router serves as this navigation system, managing what content users see based on the URL they visit.

Let's start by understanding how to set up basic routing in a React application:


// First, import necessary components from react-router-dom
import { 
    BrowserRouter, 
    Routes, 
    Route 
} from 'react-router-dom';

// Create components for different routes
function Home() {
    return <h1>Welcome to our App!</h1>;
}

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

function Contact() {
    return <h1>Contact Us</h1>;
}

// Set up the routing structure
function App() {
    return (
        <BrowserRouter>
            <Routes>
                <Route path="/" element={<Home />} />
                <Route path="/about" element={<About />} />
                <Route path="/contact" element={<Contact />} />
            </Routes>
        </BrowserRouter>
    );
}
            

Creating Layouts with Nested Routes

Just as a city might have different districts with their own layouts but sharing common elements like main roads, React Router allows you to create layouts that share common elements across multiple routes. This is achieved using the Outlet component:


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

// Create a layout component that will wrap other routes
function MainLayout() {
    return (
        <div className="app-layout">
            <header>
                <nav>
                    {/* Navigation links will go here */}
                </nav>
            </header>

            {/* Outlet renders the child route's element */}
            <main>
                <Outlet />
            </main>

            <footer>
                <p>© 2025 Our App</p>
            </footer>
        </div>
    );
}

// Set up nested routes using the layout
function App() {
    return (
        <BrowserRouter>
            <Routes>
                <Route path="/" element={<MainLayout />}>
                    <Route index element={<Home />} />
                    <Route path="about" element={<About />} />
                    <Route path="contact" element={<Contact />} />
                </Route>
            </Routes>
        </BrowserRouter>
    );
}
            

Working with Dynamic Routes and Parameters

Often, you'll need routes that can handle dynamic values, like user IDs or product names. The useParams hook helps you access these values:


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

// Component to display user profiles
function UserProfile() {
    // Extract userId from the URL
    const { userId } = useParams();
    
    return (
        <div className="user-profile">
            <h1>User Profile: {userId}</h1>
            {/* Add profile content here */}
        </div>
    );
}

// Product details component with multiple parameters
function ProductDetails() {
    const { category, productId } = useParams();
    
    return (
        <div className="product-details">
            <h1>{category} Product: {productId}</h1>
            {/* Add product details here */}
        </div>
    );
}

// Set up routes with parameters
function App() {
    return (
        <BrowserRouter>
            <Routes>
                <Route path="/" element={<MainLayout />}>
                    <Route path="users/:userId" element={<UserProfile />} />
                    <Route 
                        path="products/:category/:productId" 
                        element={<ProductDetails />} 
                    />
                </Route>
            </Routes>
        </BrowserRouter>
    );
}
            

Putting It All Together: Building a Complete Router Setup

Let's create a complete example that demonstrates all these concepts working together:


import {
    BrowserRouter,
    Routes,
    Route,
    Link,
    NavLink,
    Outlet,
    useParams,
    useNavigate
} from 'react-router-dom';

// Layout component with navigation
function Layout() {
    return (
        <div className="app">
            <header>
                <nav>
                    <NavLink 
                        to="/" 
                        end
                        className={({ isActive }) => 
                            `nav-link ${isActive ? 'active' : ''}`
                        }
                    >
                        Home
                    </NavLink>
                    <NavLink 
                        to="/products"
                        className={({ isActive }) => 
                            `nav-link ${isActive ? 'active' : ''}`
                        }
                    >
                        Products
                    </NavLink>
                </nav>
            </header>

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

// Products list with navigation
function ProductsList() {
    const navigate = useNavigate();

    const handleProductClick = (productId) => {
        navigate(`/products/${productId}`);
    };

    return (
        <div className="products">
            <h1>Our Products</h1>
            {products.map(product => (
                <div 
                    key={product.id}
                    onClick={() => handleProductClick(product.id)}
                    className="product-card"
                >
                    <h2>{product.name}</h2>
                    <p>{product.description}</p>
                </div>
            ))}
        </div>
    );
}

// Product detail page with parameters
function ProductDetail() {
    const { productId } = useParams();
    const navigate = useNavigate();
    
    // Simulate loading product data
    const product = findProduct(productId);
    
    if (!product) {
        return <Navigate to="/products" replace />;
    }

    return (
        <div className="product-detail">
            <h1>{product.name}</h1>
            <p>{product.description}</p>
            <button onClick={() => navigate(-1)}>
                Back to Products
            </button>
        </div>
    );
}

// Complete router setup
function App() {
    return (
        <BrowserRouter>
            <Routes>
                <Route path="/" element={<Layout />}>
                    <Route index element={<Home />} />
                    <Route path="products" element={<ProductsList />} />
                    <Route 
                        path="products/:productId" 
                        element={<ProductDetail />} 
                    />
                    <Route path="*" element={<NotFound />} />
                </Route>
            </Routes>
        </BrowserRouter>
    );
}