Part 29: Protected Routes & Authentication Guards

Part 29 Technology Updated September 12, 2026

Part 29: Protected Routes & Authentication Guards

Restricting access to private pages (user dashboards, account settings, checkout) based on authentication status is a critical requirement for production web applications.


1. Creating a ProtectedRoute Wrapper

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

export default function ProtectedRoute({ isAuthenticated, userRole, allowedRoles }) {
  const location = useLocation();

  // If not authenticated, redirect to login and preserve the attempted URL
  if (!isAuthenticated) {
    return <Navigate to="/login" state={{ from: location }} replace />;
  }

  // Optional: Role-based access control
  if (allowedRoles && !allowedRoles.includes(userRole)) {
    return <Navigate to="/unauthorized" replace />;
  }

  // Render the protected route's child elements
  return <Outlet />;
}

2. Protecting Entire Route Trees

import { Routes, Route } from 'react-router-dom';
import ProtectedRoute from './ProtectedRoute';

export default function App({ user }) {
  const isAuthenticated = Boolean(user);

  return (
    <Routes>
      {/* Public Routes */}
      <Route path="/" element={<HomePage />} />
      <Route path="/login" element={<LoginPage />} />

      {/* Protected Routes */}
      <Route element={<ProtectedRoute isAuthenticated={isAuthenticated} />}>
        <Route path="/dashboard" element={<DashboardPage />} />
        <Route path="/profile" element={<ProfilePage />} />
      </Route>
    </Routes>
  );
}

3. Redirecting Back After Login

Inside your LoginPage:

import { useNavigate, useLocation } from 'react-router-dom';

export default function LoginPage({ onLogin }) {
  const navigate = useNavigate();
  const location = useLocation();

  // Return to the page the user originally tried to visit, or fallback to '/'
  const from = location.state?.from?.pathname || '/dashboard';

  const handleLogin = () => {
    onLogin();
    navigate(from, { replace: true });
  };

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

Key Takeaways

  • Wrap private views in a ProtectedRoute component that checks authentication status.
  • Save location in state to redirect users back to their intended destination after signing in.
  • Use <Navigate replace /> to avoid trapping users in infinite redirect loops.

In Part 30, we enter Module 5 to explore State Management Architecture: Local vs Global vs Server State!