Skip to main content

Routing in React

Routing decides which React component should appear for the current URL.

/                  -> Home
/about -> About
/products/42 -> Product 42
/dashboard/profile -> Dashboard Profile

In a React Single Page Application (SPA), the app usually loads once. Internal navigation then changes the URL and renders the matching component without requesting a completely new HTML page.

One-line idea: URL changes → React Router finds a match → the matching component renders.

Core Concepts

Why Routing Is Needed

Without client-side routing, moving to another page generally causes the browser to request and load a new document.

With React routing:

User selects a link

The URL changes

React Router matches the path

Only the relevant UI changes
Traditional page navigationReact SPA navigation
Requests a new HTML documentKeeps the current app running
Reloads the full pageRenders the matched component
Recreates the complete UIPreserves shared UI such as a navbar

Install React Router

React does not include routing by default. For a browser-based React app, install react-router-dom:

npm install react-router-dom

Configure the Router

Wrap the application with BrowserRouter so components inside it can use routing features.

main.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App";

ReactDOM.createRoot(document.getElementById("root")).render(
<BrowserRouter>
<App />
</BrowserRouter>,
);

Then map URL paths to elements using Routes and Route:

App.jsx
import { Route, Routes } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
import Login from "./pages/Login";

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

How to read a route:

<Route path="/about" element={<About />} />
  • path is the URL pattern.
  • element is the UI rendered when that pattern matches.

Use Link for navigation between routes inside the React application.

Navbar.jsx
import { Link } from "react-router-dom";

export default function Navbar() {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/login">Login</Link>
</nav>
);
}
UseBest for
<Link to="/about">Internal navigation without a full page reload
<a href="https://example.com">An external website or deliberate document navigation

Core React Router APIs

APIPurpose
BrowserRouterConnects the app to the browser URL
RoutesHolds the route definitions
RouteMaps a path to an element
LinkNavigates to another internal route
OutletRenders the matched child route
useParamsReads dynamic values from the URL
NavigateRedirects during rendering
useNavigateNavigates from event or application logic
path="*"Matches an otherwise unmatched URL

Practical Routing Patterns

Shared Layout with Outlet

A navbar and footer should not be repeated in every page component. Place them in a layout and use Outlet as the position where child routes render.

Layout.jsx
import { Outlet } from "react-router-dom";
import Navbar from "./Navbar";
import Footer from "./Footer";

export default function Layout() {
return (
<>
<Navbar />

<main>
<Outlet />
</main>

<Footer />
</>
);
}
App.jsx
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Route>
</Routes>

Result:

Navbar  -> remains visible
Outlet -> changes between Home and About
Footer -> remains visible

Nested Routes

Nested routes represent screens that belong to a common parent section.

/dashboard/profile
/dashboard/settings
/dashboard/orders

The parent component must contain an Outlet for its matched child.

Dashboard.jsx
import { Link, Outlet } from "react-router-dom";

export default function Dashboard() {
return (
<section>
<h1>Dashboard</h1>

<nav>
<Link to="profile">Profile</Link>
<Link to="settings">Settings</Link>
</nav>

<Outlet />
</section>
);
}
<Route path="/dashboard" element={<Dashboard />}>
<Route path="profile" element={<Profile />} />
<Route path="settings" element={<Settings />} />
</Route>

Notice that child paths are relative:

Parent: /dashboard
Child: profile
Final: /dashboard/profile

Dynamic Routes and useParams

Use a dynamic segment when the same page structure displays different data.

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

Here, :id is a route parameter.

ProductDetails.jsx
import { useParams } from "react-router-dom";

export default function ProductDetails() {
const { id } = useParams();

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

For /products/42, useParams() returns an object containing id: "42".

Common use cases:

  • product details: /products/:id
  • user profiles: /users/:userId
  • order details: /orders/:orderId
  • blog posts: /posts/:slug

Protected Routes with Navigate

A protected route checks whether the user may enter a screen. If the check fails, render Navigate to redirect the user.

ProtectedRoute.jsx
import { Navigate } from "react-router-dom";

export default function ProtectedRoute({ isAuthenticated, children }) {
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}

return children;
}
<Route
path="/dashboard"
element={
<ProtectedRoute isAuthenticated={isAuthenticated}>
<Dashboard />
</ProtectedRoute>
}
/>

Decision flow:

Authenticated?
├─ Yes -> render Dashboard
└─ No -> redirect to /login

A route guard controls frontend navigation. Protected data and actions must still be authorized by the backend.

Programmatic Navigation with useNavigate

Use useNavigate when navigation should happen after an action such as login, form submission, or checkout.

Login.jsx
import { useNavigate } from "react-router-dom";

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

function handleLogin() {
// Complete authentication first.
navigate("/dashboard");
}

return <button onClick={handleLogin}>Log in</button>;
}

Remember:

Link        -> navigation initiated from rendered UI
useNavigate -> navigation initiated from JavaScript logic
Navigate -> redirect expressed during rendering

404 Route

Place a wildcard route after the known route definitions to handle unmatched URLs.

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

This prevents users from seeing a blank screen when a URL does not exist.

Route-Level Lazy Loading

Loading every page in the initial JavaScript bundle can slow down a large application. lazy loads a page component when it is needed, while Suspense displays fallback UI.

App.jsx
import { lazy, Suspense } from "react";
import { Route, Routes } from "react-router-dom";

const Home = lazy(() => import("./pages/Home"));
const Dashboard = lazy(() => import("./pages/Dashboard"));

export default function App() {
return (
<Suspense fallback={<p>Loading page...</p>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
);
}
Route is visited

Its component bundle is requested

Fallback appears while loading

The page component renders

Routing and SEO

A client-rendered SPA may initially return very little page content in its HTML:

<div id="root"></div>

The visible content is added after JavaScript runs. This can make indexing more difficult for search-sensitive pages.

Each important route should also define suitable metadata, such as:

  • page title
  • description
  • Open Graph information

For content where search visibility is important, route-specific metadata and server-rendered or pre-rendered HTML are commonly considered.

Benefits, Trade-Offs, and Common Mistakes

Benefits

  • Smooth internal navigation without a complete page reload.
  • URLs can represent specific application screens.
  • Shared layouts keep common UI mounted.
  • Nested routes mirror sections such as dashboards.
  • Dynamic routes reuse one page for many records.
  • Route-level lazy loading can reduce the initial bundle.

Trade-offs

  • Routing adds configuration and route structure to maintain.
  • Large client-rendered apps can send more JavaScript initially.
  • Search-sensitive pages need deliberate metadata and rendering decisions.
  • A frontend protected route is not a replacement for backend authorization.

Common mistakes

MistakeBetter approach
Using <a> for every internal pageUse Link for SPA routes
Repeating navbar and footer on each pageCreate a layout with Outlet
Forgetting Outlet in a parent routeAdd it where child UI should render
Creating one route per productUse /products/:id
Calling navigation before login succeedsNavigate after successful authentication
Omitting an unmatched-route fallbackAdd <Route path="*" ... />
Loading every large page immediatelyLazy-load route components where useful

Interview Revision

Quick Revision Checklist

  • Routing maps a URL to a React element.
  • BrowserRouter enables browser-based routing.
  • Routes contains route definitions; Route maps a path to an element.
  • Link changes internal routes without a full document reload.
  • Shared layouts use Outlet to render child routes.
  • Nested routes model related screens under a parent path.
  • Dynamic segments such as :id are read with useParams.
  • Navigate handles declarative redirects.
  • useNavigate handles navigation from application logic.
  • path="*" renders a 404 fallback.
  • lazy and Suspense support route-level code splitting.
  • Important routes need appropriate metadata for SEO and sharing.

Frequently Asked Interview Questions

1. What is routing in React?

Routing is the mechanism that matches the current URL with a React element and renders the corresponding screen without reloading the entire SPA.

2. Why use Link instead of an anchor tag?

Link performs internal client-side navigation and preserves the running application. A normal anchor usually requests a new document and reloads the page.

3. What is the purpose of Outlet?

Outlet is the placeholder inside a parent layout where the currently matched child route renders.

4. What is the difference between Navigate and useNavigate?

Navigate redirects as part of rendering. useNavigate returns a function used to navigate from event handlers or other application logic.

5. How do dynamic routes work?

A path such as /products/:id marks id as dynamic. When the URL is /products/42, useParams() provides the value 42 to the component.

6. How would you protect a dashboard route?

Wrap the dashboard element in a component that checks authentication. Render the dashboard when authenticated; otherwise render Navigate to redirect to the login route. Backend APIs must independently authorize protected data and operations.

7. How can routing improve performance?

Lazy-load large page components so their JavaScript is fetched only when the related route is visited. Display fallback UI with Suspense while loading.

Memory Trick

B-R-R-L-O-P-N

BrowserRouter -> enables routing
Routes -> holds the route map
Route -> connects path and UI
Link -> moves between routes
Outlet -> renders a child route
Params -> reads dynamic URL values
Navigate -> redirects the user

One-Line Summary

Routing keeps the URL and visible React screen in sync without reloading the entire application.

Final Mental Model

BrowserRouter watches the URL

Routes searches the route map

Route selects the matching element

Layout stays; Outlet changes

Params provide dynamic URL data

Navigate or useNavigate changes the route when required