Advanced Routing Strategies with React Router

Routing is one of the essential aspects of building single-page applications (SPAs) in React. React Router is the most widely used library for handling navigation in React apps. In this chapter, we will dive into advanced routing strategies using React Router, starting from the basic setup to more sophisticated techniques like route nesting, dynamic routing, lazy loading, and handling authentication and authorization in routes.We will explore how to make your application more robust, performant, and secure through these routing techniques.

Overview of React Router

What is React Router?

React Router is a declarative routing library for React that allows developers to handle navigation and rendering of different components based on the URL. React Router enables you to:

  • Define routes that map paths (URLs) to components.
  • Handle navigation without refreshing the page.
  • Pass data via URL parameters or query strings.
  • Create nested routes and dynamic routes.

You can install React Router using:

				
					npm install react-router-dom

				
			

Basic Example:

				
					import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

const App = () => {
  return (
    <Router>
      <Switch>
        <Route exact path="/" component={HomePage} />
        <Route path="/about" component={AboutPage} />
        <Route path="/contact" component={ContactPage} />
      </Switch>
    </Router>
  );
};

				
			

In this basic example, React Router renders different components (HomePage, AboutPage, ContactPage) based on the current URL path.

Dynamic and Nested Routes

Dynamic Routes

Dynamic routing allows you to create routes with URL parameters that can be passed to components as dynamic values. This is useful when displaying data based on a specific ID or parameter (e.g., user profiles, product details).

To manage dynamic data. When state changes, the component re-renders, reflecting the new data. Basic State Management with useState

Example:

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

const ProductPage = () => {
  const { productId } = useParams(); // Extract productId from the URL

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

const App = () => {
  return (
    <Router>
      <Switch>
        <Route path="/product/:productId" component={ProductPage} />
      </Switch>
    </Router>
  );
};

				
			

In this example, when the user navigates to /product/123, the ProductPage component will receive 123 as the productId parameter.

Nested Routes

React Router also supports nested routes, which allows you to define sub-routes within parent components. This is useful when creating hierarchical layouts or dashboards.

Example:

				
					const UserProfile = () => {
  return (
    <div>
      <h1>User Profile</h1>
      <Switch>
        <Route path="/profile/details" component={ProfileDetails} />
        <Route path="/profile/settings" component={ProfileSettings} />
      </Switch>
    </div>
  );
};

const App = () => {
  return (
    <Router>
      <Switch>
        <Route path="/profile" component={UserProfile} />
      </Switch>
    </Router>
  );
};

				
			

In this example, navigating to /profile/details or /profile/settings will render the respective child components (ProfileDetails or ProfileSettings) inside the UserProfile component.

Route Guards (Authentication & Authorization)

Protecting Routes (Private Routes)

A common scenario in SPAs is the need to protect certain routes, ensuring that only authenticated users can access them. This can be achieved by creating Private Routes using React Router.

Example:

				
					import { Redirect, Route } from 'react-router-dom';

const PrivateRoute = ({ component: Component, isAuthenticated, ...rest }) => (
  <Route
    {...rest}
    render={props =>
      isAuthenticated ? (
        <Component {...props} />
      ) : (
        <Redirect to="/login" />
      )
    }
  />
);

const App = () => {
  const isAuthenticated = true; // Change this based on actual authentication logic

  return (
    <Router>
      <Switch>
        <PrivateRoute path="/dashboard" component={Dashboard} isAuthenticated={isAuthenticated} />
        <Route path="/login" component={LoginPage} />
      </Switch>
    </Router>
  );
};

				
			

In this example, PrivateRoute checks whether the user is authenticated. If not, they are redirected to the /login page.

Role-Based Authorization

You can also implement role-based authorization by checking user roles and permissions before rendering routes.

				
					const AdminRoute = ({ component: Component, userRole, ...rest }) => (
  <Route
    {...rest}
    render={props =>
      userRole === 'admin' ? (
        <Component {...props} />
      ) : (
        <Redirect to="/not-authorized" />
      )
    }
  />
);

const App = () => {
  const userRole = 'admin'; // User's role fetched from context or state

  return (
    <Router>
      <Switch>
        <AdminRoute path="/admin" component={AdminDashboard} userRole={userRole} />
        <Route path="/not-authorized" component={NotAuthorizedPage} />
      </Switch>
    </Router>
  );
};


  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

				
			

Code-Splitting and Lazy Loading Routes

Code-Splitting in React

React applications can become large over time, which can lead to slow load times. Code-splitting allows you to split your code into smaller chunks that are loaded on demand, improving performance.

React supports code-splitting through the React.lazy function, which lets you load components lazily.

Example:

				
					import React, { Suspense } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

const HomePage = React.lazy(() => import('./HomePage'));
const AboutPage = React.lazy(() => import('./AboutPage'));

const App = () => {
  return (
    <Router>
      <Suspense fallback={<div>Loading...</div>}>
        <Switch>
          <Route exact path="/" component={HomePage} />
          <Route path="/about" component={AboutPage} />
        </Switch>
      </Suspense>
    </Router>
  );
};

				
			

In this example, HomePage and AboutPage components are only loaded when the user navigates to their respective routes. The Suspense component displays a fallback UI (e.g., “Loading…”) while the components are being loaded.

Lazy Loading with Nested Routes

Lazy loading can also be applied to nested routes to optimize performance further, especially in large applications where different sections have their own set of nested routes.

Route-Based Data Fetching

In a modern React app, data is often fetched based on the route the user navigates to. You can use useEffect in React components to fetch data when the component mounts or when the route changes.

Example:

				
					import { useParams } from 'react-router-dom';
import { useEffect, useState } from 'react';

const UserPage = () => {
  const { userId } = useParams();
  const [userData, setUserData] = useState(null);

  useEffect(() => {
    fetch(`https://api.example.com/users/${userId}`)
      .then(response => response.json())
      .then(data => setUserData(data));
  }, [userId]);

  return (
    <div>
      {userData ? <div>User: {userData.name}</div> : <div>Loading...</div>}
    </div>
  );
};

const App = () => {
  return (
    <Router>
      <Switch>
        <Route path="/user/:userId" component={UserPage} />
      </Switch>
    </Router>
  );
};

				
			

In this example, data is fetched dynamically based on the userId parameter from the URL.

Handling 404s and Fallback Routes

It’s essential to handle routes that do not exist in your application and provide the user with a meaningful 404 page.

Example:

				
					const NotFoundPage = () => {
  return <div>404 - Page Not Found</div>;
};

const App = () => {
  return (
    <Router>
      <Switch>
        <Route exact path="/" component={HomePage} />
        <Route path="/about" component={AboutPage} />
        <Route path="*" component={NotFoundPage} /> {/* Catch-all route for 404 */}
      </Switch>
    </Router>
  );
};

				
			

The Route path="*" is a catch-all route that will render the NotFoundPage component for any undefined routes.

Routing is a crucial part of any React application, and React Router offers powerful tools to handle routing efficiently. By using advanced techniques such as dynamic routing, nested routes, authentication and authorization guards, lazy loading, and route-based data fetching, you can build a highly scalable and performant React application. Happy coding !❤️

Table of Contents

Contact here

Copyright © 2025 Diginode

Made with ❤️ in India