Protected Routes
Protected routes restrict access to authenticated or authorized users. Unauthenticated visitors redirect to login; unauthorized roles see forbidden UI. This lesson implements auth guards with React Router v6.
Route Guard Pattern Wrap protected elements in a component that checks auth state. If valid, render <Outlet /> or children; otherwise <Navigate to="/login" replace />.
Store intended destination in location state so login can redirect back after success.
function ProtectedRoute() {
const { user, loading } = useAuth();
const location = useLocation();
if (loading) return <Spinner />;
if (!user) return <Navigate to="/login" state={{ from: location }} replace />;
return <Outlet />;
}
<Route element={<ProtectedRoute />}>
<Route path="/dashboard" element={<Dashboard />} />
</Route> state={{ from: location }} enables post-login redirect to originally requested page.
Role-Based Guards function AdminRoute() {
const { user } = useAuth();
if (user?.role !== 'admin') return <Navigate to="/403" />;
return <Outlet />;
} Show loading UI while auth state resolves — avoid flash redirect. Use layout route guards with Outlet for route groups. Handle token expiry with global fetch interceptor + logout. Never rely on client guards alone — secure APIs server-side. Protected Route Patterns Common auth routing approaches.
Pattern Implementation Auth wrapper <ProtectedRoute /> + Outlet Redirect login <Navigate to="/login" /> Return URL location.state.from Role check Compare user.role before render Lazy auth load Spinner until auth resolves Server truth API 401 → logout handler
Post-Login Redirect After successful login, navigate to location.state?.from?.pathname ?? '/dashboard'.
const location = useLocation();
const from = location.state?.from?.pathname || '/';
navigate(from, { replace: true }); Client Guards vs Server Security Client-side route guards improve UX only. Always validate sessions and permissions on the server for every API request.
Common Mistakes Redirect flash before auth loading completes. Trusting client-side role checks for security. Losing return URL after login redirect chain. Not handling expired tokens globally. Key Takeaways Wrap routes with auth check components using Outlet. Redirect unauthenticated users with Navigate + state. Show loading while auth initializes. Server must enforce authorization — client guards are UX only.
Pro Tip
Centralize auth in one AuthProvider + ProtectedRoute — avoid sprinkling checks in every page component.
Route Params Go to next item