Nested Routes & Route Guards in React Router Explained with Examples
When building modern React applications, you often have complex layouts like dashboards, profiles, or admin panels — where multiple pages share common UI parts like a sidebar, header, or footer.
You also need security controls — for example, only logged-in users should access certain routes.
React Router provides two powerful tools for this:
- Nested Routes – for structured, hierarchical layouts. 
- Route Guards – for protecting private or restricted pages. 
Nested Routes
What Are Nested Routes?
Nested routes allow you to organize routes in a parent-child hierarchy.
This helps when multiple pages share a common layout or structure.
Example:
/dashboard
/dashboard/profile
/dashboard/settings Here, /dashboard is the parent route, and the other two are child routes.
Step-by-Step Example
App Structure
import React from "react";
import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router-dom";
function Dashboard() {
  return (
    <div>
      <h2>Dashboard Layout</h2>
      <nav>
        <Link to="profile">Profile</Link> |{" "}
        <Link to="settings">Settings</Link>
      </nav>
      {/* Child Routes will render here */}
      <Outlet />
    </div>
  );
}
function Profile() {
  return <h3> User Profile Page</h3>;
}
function Settings() {
  return <h3>⚙️ Account Settings Page</h3>;
}
function App() {
  return (
    <BrowserRouter>
      <Routes>
        {/* Parent Route */}
        <Route path="/dashboard" element={<Dashboard />}>
          {/* Nested Child Routes */}
          <Route path="profile" element={<Profile />} />
          <Route path="settings" element={<Settings />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}
export default App;
Explanation
- Dashboardis the parent route.
- Profileand- Settingsare child routes inside it.
- The - <Outlet />component acts as a placeholder where child components render.
So when you visit /dashboard/profile, React Router first loads Dashboard, then inserts the Profile component into <Outlet />.
Why Use Nested Routes?
- Reuse shared layouts (like sidebars, navbars).
- Cleaner URL hierarchy.
- Easier to maintain modular structure.
- Improves navigation flow and app organization
Nested Routes with Default Child
You can set a default child route that renders when no sub-path is specified.
<Route path="/dashboard" element={<Dashboard />}>  <Route index element={<Profile />} /> {/* Default route */}  <Route path="settings" element={<Settings />} />
</Route>  When user visits /dashboard, the Profile page loads by default.
Route Guards (Protected Routes)
What Are Route Guards?
Route Guards prevent unauthorized users from accessing certain pages.
For example:
- Only logged-in users can access - /dashboard.
- Guests should be redirected to - /login.
Basic Protected Route Example
import React from "react";
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
function Dashboard() {
  return <h2>Welcome to Dashboard 🚀</h2>;
}
function Login() {
  return <h2>Please Log In 🔐</h2>;
}
function ProtectedRoute({ isAuthenticated, children }) {
  if (!isAuthenticated) {
    return <Navigate to="/login" replace />;
  }
  return children;
}
function App() {
  const isAuthenticated = false; // simulate login state
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/login" element={<Login />} />
        <Route
          path="/dashboard"
          element={
            <ProtectedRoute isAuthenticated={isAuthenticated}>
              <Dashboard />
            </ProtectedRoute>
          }
        />
      </Routes>
    </BrowserRouter>
  );
}
export default App;
Explanation
- ProtectedRouteis a custom component that checks if the user is logged in.
- If - isAuthenticatedis false, it redirects to- /loginusing- <Navigate />.
- Otherwise, it renders the child component ( - Dashboard).
Combining Nested Routes with Route Guards
You can protect entire route groups like dashboards with multiple subpages.
import { BrowserRouter, Routes, Route, Navigate, Outlet } from "react-router-dom";
function ProtectedLayout({ isAuthenticated }) {
  return isAuthenticated ? <Outlet /> : <Navigate to="/login" />;
}
function Dashboard() {
  return (
    <div>
      <h2> Dashboard</h2>
      <Outlet />
    </div>
  );
}
function Profile() {
  return <h3> Profile Page</h3>;
}
function Settings() {
  return <h3> Settings Page</h3>;
}
function Login() {
  return <h2> Login Page</h2>;
}
function App() {
  const isAuthenticated = true; // simulate logged-in user
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/login" element={<Login />} />
        {/* Protected Routes */}
        <Route element={<ProtectedLayout isAuthenticated={isAuthenticated} />}>
          <Route path="/dashboard" element={<Dashboard />}>
            <Route path="profile" element={<Profile />} />
            <Route path="settings" element={<Settings />} />
          </Route>
        </Route>
      </Routes>
    </BrowserRouter>
  );
}
export default App;
Explanation:
- The - ProtectedLayoutwraps around all dashboard routes.
- If not logged in, the user is redirected to - /login.
- If logged in, child routes render inside - <Outlet />.
Tips & Best Practices
- Use Layout Routes for shared UI like headers or sidebars.
- Keep Protected Routes modular (don’t repeat guard logic everywhere).
- Use Context or Redux to store authentication state globally.
- Handle loading states if authentication check depends on an API.
Common Mistakes
- Forgetting <Outlet />→ child routes won’t render.
- Not using <Navigate />for redirects → unauthorized users see blank pages.
- Using nested paths without relative URLs → links may break.
- Forgetting to define a default (index) route → parent renders empty.
