Skip to main content

React Router - Routing

React Router - Routing

React Router provides a declarative way to manage navigation in React applications. It enables developers to define routes using a configuration-based approach, ensuring clean and maintainable code. This guide covers essential routing concepts with examples.


Configuring Routes

Routes in React Router are defined using the <Route> component inside a <Routes> wrapper. Each <Route> specifies a path and an associated component.

import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from "./Home";
import About from "./About";

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

export default App;

Components Used:

  • BrowserRouter: Wraps the application and enables routing.
  • Routes: Contains all route definitions.
  • Route: Defines individual routes mapping paths to components.
  • Home and About: Simple components representing different pages.

Not Found Routes

A Not Found route handles cases where the user navigates to an undefined path. The <Route path="*" /> matches any unknown route and renders a fallback component.


import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import NotFound from "./NotFound";

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

export default App;

Components Used:

  • NotFound: A fallback component displaying a "Page Not Found" message.
  • Route path="*": Catches all unmatched routes.

Nested Routes

Nested routes allow structuring child routes within a parent route. The child components will be rendered inside the parent component.


import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
import Dashboard from "./Dashboard";
import Settings from "./Settings";

function AdminLayout() {
return (
<div>
<h2>Admin Panel</h2>
<Outlet />
</div>
);
}

function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/admin" element={<AdminLayout />}>
<Route path="dashboard" element={<Dashboard />} />
<Route path="settings" element={<Settings />} />
</Route>
</Routes>
</BrowserRouter>
);
}

export default App;

Components Used:

  • AdminLayout: Acts as a wrapper for admin pages.
  • Outlet: Placeholder for rendering child routes inside the parent component.
  • Dashboard and Settings: Child components rendered within AdminLayout.

Layout Routes

Layout routes allow wrapping multiple pages with common UI elements like navigation bars or footers. Additionally, they can handle authentication and redirects.


import { BrowserRouter, Routes, Route, Outlet, Navigate } from "react-router-dom";
import Navbar from "./Navbar";
import Home from "./Home";
import About from "./About";
import Login from "./Login";
import Dashboard from "./Dashboard";

const isAuthenticated = () => {
return localStorage.getItem("auth") === "true";
};

function Layout() {
return (
<div>
<Navbar />
<Outlet />
</div>
);
}

function PrivateRoute({ children }) {
if (!isAuthenticated()) {
return <Navigate to="/login" />;
}
return children ? (
children
) : (
<div>
<Navbar />
<Outlet />
</div>
);
}

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

<Route path="login" element={<Login />} />

<Route
path="dashboard"
element={
<PrivateRoute>
<Dashboard />
</PrivateRoute>
}
/>
</Routes>
</BrowserRouter>
);
}

export default App;

Components Used:

  • Layout: Defines a shared layout with a Navbar and an Outlet.
  • Navbar: Navigation component available on all pages.
  • Outlet: Ensures child routes (like Home and About) render inside Layout.
  • PrivateRoute: Checks authentication and redirects unauthenticated users to the login page.
  • Navigate: Used for redirection in case authentication fails.
  • Login and Dashboard: Separate pages for authentication and user dashboard.

Optional Segments

An optional segment is a part of the route path that may or may not be present in the URL. You can mark a segment as optional by appending a ? to the segment in the route path.


1. Optional Parameter Segment

<Route path=":lang?/categories" element={<Categories />} />

This matches:

  • /categories
  • /en/categories

Inside your component:

const { lang } = useParams(); // 'lang' could be undefined

2. Optional Static Segment

<Route path="users/:userId/edit?" element={<User />} />

This matches:

  • /users/123
  • /users/123/edit

Inside the component: To detect if the path ends with /edit, you can use useLocation

import { useLocation } from "react-router-dom";

const { pathname } = useLocation();
const isEditMode = pathname.endsWith("/edit");

Index Routes

Index routes are default child routes that render when the parent route is matched.


<Route path="/dashboard" element={<DashboardLayout />}>
<Route index element={<DashboardHome />} />
<Route path="stats" element={<Stats />} />
</Route>

Components Used:

  • DashboardLayout: Wrapper for dashboard pages.
  • DashboardHome: Default child component rendered at /dashboard.
  • Stats: Additional subpage for statistics.

Lazy Loading Routes

React provides React.lazy() to dynamically import components, allowing for code-splitting and on-demand loading.

Using lazy loading improves performance by reducing the initial bundle size and loading components only when they are needed.


Below is an example of how to use lazy loading with Suspense:

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

const Home = lazy(() => import("./Home"));
const About = lazy(() => import("./About"));
const NotFound = lazy(() => import("./NotFound"));
const Dashboard = lazy(() => import("./Dashboard"));
const Settings = lazy(() => import("./Settings"));

function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<NotFound />} />
<Route path="/admin" element={<AdminLayout />}>
<Route path="dashboard" element={<Dashboard />} />
<Route path="settings" element={<Settings />} />
</Route>
</Routes>
</Suspense>
</BrowserRouter>
);
}

export default App;

Explanation:

  • React.lazy(() => import(...)): Dynamically imports components only when they are needed.
  • Suspense: Wraps lazy-loaded components and shows a fallback (Loading...) while they are being loaded.
  • Smaller Initial Bundle: Components are loaded only when users navigate to them.

Lazy loading is particularly useful for large applications, reducing initial page load times and improving performance.