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 navigation | React SPA navigation |
|---|---|
| Requests a new HTML document | Keeps the current app running |
| Reloads the full page | Renders the matched component |
| Recreates the complete UI | Preserves 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.
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:
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 />} />
pathis the URL pattern.elementis the UI rendered when that pattern matches.
Navigate with Link
Use Link for navigation between routes inside the React application.
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>
);
}
| Use | Best 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
| API | Purpose |
|---|---|
BrowserRouter | Connects the app to the browser URL |
Routes | Holds the route definitions |
Route | Maps a path to an element |
Link | Navigates to another internal route |
Outlet | Renders the matched child route |
useParams | Reads dynamic values from the URL |
Navigate | Redirects during rendering |
useNavigate | Navigates 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.
import { Outlet } from "react-router-dom";
import Navbar from "./Navbar";
import Footer from "./Footer";
export default function Layout() {
return (
<>
<Navbar />
<main>
<Outlet />
</main>
<Footer />
</>
);
}
<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.
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.
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.
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.
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.
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
| Mistake | Better approach |
|---|---|
Using <a> for every internal page | Use Link for SPA routes |
| Repeating navbar and footer on each page | Create a layout with Outlet |
Forgetting Outlet in a parent route | Add it where child UI should render |
| Creating one route per product | Use /products/:id |
| Calling navigation before login succeeds | Navigate after successful authentication |
| Omitting an unmatched-route fallback | Add <Route path="*" ... /> |
| Loading every large page immediately | Lazy-load route components where useful |
Interview Revision
Quick Revision Checklist
- Routing maps a URL to a React element.
BrowserRouterenables browser-based routing.Routescontains route definitions;Routemaps a path to an element.Linkchanges internal routes without a full document reload.- Shared layouts use
Outletto render child routes. - Nested routes model related screens under a parent path.
- Dynamic segments such as
:idare read withuseParams. Navigatehandles declarative redirects.useNavigatehandles navigation from application logic.path="*"renders a 404 fallback.lazyandSuspensesupport 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