React Mastery / Routing (React Router)

Protected and Private Route

🧠 What Are Protected / Private Routes?

A protected route is a route that:

  • Can be accessed only if a condition is met

  • Usually depends on:

    • Authentication (logged in)

    • Authorization (role-based access)

Examples:

  • Dashboard → only logged-in users

  • Admin panel → only admins

  • Profile page → only authenticated users


❓ Why Do We Need Protected Routes?

Without protection:

  • Anyone can access sensitive URLs directly

  • Example:

    /dashboard
    /admin

With protected routes:

  • Unauthenticated users are redirected

  • App becomes secure and predictable


🧱 Core Idea (How It Works)

  1. User tries to access a route

  2. App checks authentication status

  3. If authenticated → show page

  4. If not authenticated → redirect to login


🧩 Common Protection Strategies

Strategy

Use Case

Auth state (boolean)

Simple login check

Token check

JWT-based auth

Role check

Admin / User access

API validation

High-security apps


🛠️ Basic Example: Auth State

Assume we have an auth state:

const isAuthenticated = true; // or false\

🔒 Creating a Private Route Component

Why Create One?

  • Avoid repeating auth logic

  • Keep routes clean

  • Centralize access control

PrivateRoute.jsx

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

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

  return children;
}

export default PrivateRoute;

🧠 Explanation

  • children → protected component

  • Navigate → redirects user

  • replace → prevents going back to protected page

🔗 Using PrivateRoute in Routes

App.jsx

import { Routes, Route } from 'react-router-dom';
import PrivateRoute from './PrivateRoute';
import Dashboard from './pages/Dashboard';
import Login from './pages/Login';

const isAuthenticated = false;

function App() {
  return (
    <Routes>
      <Route path="/login" element={<Login />} />

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

export default App;

🔄 What Happens Here?

  • User visits /dashboard

  • App checks isAuthenticated

  • ❌ If false → redirected to /login

  • ✅ If true → Dashboard renders


🧪 Protected Route Flow

User → Protected URL
   ↓
Auth Check
   ↓
❌ Not Logged In → Login Page
✅ Logged In → Requested Page

🧱 Protecting Nested Routes (Layouts)

Very common in dashboards 👇

ProtectedLayout.jsx

import { Navigate, Outlet } from 'react-router-dom';

function ProtectedLayout({ isAuthenticated }) {
  if (!isAuthenticated) {
    return <Navigate to="/login" replace />;
  }

  return <Outlet />;
}

export default ProtectedLayout;

Using It in Routes

<Route element={<ProtectedLayout isAuthenticated={isAuthenticated} />}>
  <Route path="/dashboard" element={<DashboardLayout />}>
    <Route index element={<DashboardHome />} />
    <Route path="profile" element={<Profile />} />
    <Route path="settings" element={<Settings />} />
  </Route>
</Route>

📌 All dashboard routes are now protected.


🛡️ Role-Based Protected Routes

Example: Admin Only Access

function AdminRoute({ role, children }) {
  if (role !== 'admin') {
    return <Navigate to="/unauthorized" replace />;
  }

  return children;
}

Usage:

<Route
  path="/admin"
  element={
    <AdminRoute role={userRole}>
      <AdminDashboard />
    </AdminRoute>
  }
/>

🔁 Redirect After Login (Very Important)

🧠 Goal

If a user is not logged in and tries to open a protected page,

redirect to login and send them back after login.

1️⃣ Protected Route (Save intended page)

// ProtectedRoute.jsx
import { Navigate, useLocation } from 'react-router-dom';

function ProtectedRoute({ isAuthenticated, children }) {
  const location = useLocation();

  if (!isAuthenticated) {
    return (
      <Navigate
        to="/login"
        state={{ from: location.pathname }}
        replace
      />
    );
  }

  return children;
}

export default ProtectedRoute;

What this does:

  • Saves the page user wanted (location.pathname)

  • Redirects to /login

2️⃣ Login Page (Redirect back)

// Login.jsx
import { useLocation, useNavigate } from 'react-router-dom';

function Login() {
  const navigate = useNavigate();
  const location = useLocation();

  const handleLogin = () => {
    // assume login is successful
    const redirectTo = location.state?.from || '/dashboard';
    navigate(redirectTo, { replace: true });
  };

  return <button onClick={handleLogin}>Login</button>;
}

export default Login;

What this does:

  • Reads the saved route from state.from

  • Redirects back to it

  • Falls back to /dashboard if none exists

🧪 Example Flow

  • User opens /dashboard/profile

  • Redirected to /login

  • Clicks Login

  • Redirected back to /dashboard/profile ✅


⚠️ Common Mistakes

  • ❌ Using protected logic inside components only

  • ❌ Forgetting replace in <Navigate />

  • ❌ Protecting UI but not routes

  • ❌ Exposing URLs directly


✅ Best Practices

  • Protect routes, not just UI

  • Use layout-based protection for dashboards

  • Keep auth logic centralized

  • Combine with backend validation

  • Prefer layout protection for nested routes


🧠 Mental Model (Easy to Remember)

Public routes → anyone

Private routes → authenticated users

Protected routes → condition-based access