Protected Routes & Auth Guards
Redirect unauthenticated users to login using wrapper components or loader-based guards.
Protected Routes & Auth Guards is a free React Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the React Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Why Protect Routes?
Some pages (dashboard, profile, admin) should only be accessible to authenticated users. React Router provides two approaches: wrapper components and loader-based guards.
Auth State Shape
First, establish an auth context that components can read to know if the user is logged in.
const AuthContext = React.createContext(null);
function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const login = async (creds) => { const u = await apiLogin(creds); setUser(u); };
const logout = () => setUser(null);
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
}
export const useAuth = () => useContext(AuthContext);Wrapper Component Guard
Create a PrivateRoute component that checks auth and redirects to login if the user is not authenticated.
import { Navigate, Outlet } from 'react-router-dom';
function PrivateRoute() {
const { user } = useAuth();
if (!user) return <Navigate to="/login" replace />;
return <Outlet />;
}
// Usage in router:
{ element: <PrivateRoute />, children: [
{ path: '/dashboard', element: <Dashboard /> },
]}Preserving the Redirect URL
Store the attempted URL so you can send the user back after login. Pass it via state on the Navigate component.
function PrivateRoute() {
const { user } = useAuth();
const location = useLocation();
if (!user) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
return <Outlet />;
}
// In login handler:
const location = useLocation();
const from = location.state?.from?.pathname || '/';
navigate(from, { replace: true });Loader-Based Guard (Data Router)
In the Data Router API, add an auth check to the route's loader. Throw a redirect if the user is unauthenticated.
import { redirect } from 'react-router-dom';
function requireAuth() {
const token = localStorage.getItem('token');
if (!token) throw redirect('/login');
return token;
}
const router = createBrowserRouter([
{
path: '/dashboard',
loader: requireAuth,
element: <Dashboard />,
},
]);Role-Based Guards
Extend the guard to check roles. Redirect to an unauthorized page if the user lacks the required role.
function AdminRoute() {
const { user } = useAuth();
if (!user) return <Navigate to="/login" replace />;
if (user.role !== 'admin') return <Navigate to="/unauthorized" replace />;
return <Outlet />;
}Loading State During Auth Check
If auth state is loaded asynchronously (e.g., from a server), show a spinner while the check is pending to avoid a flash of the login page.
function PrivateRoute() {
const { user, isLoading } = useAuth();
if (isLoading) return <Spinner />;
if (!user) return <Navigate to="/login" replace />;
return <Outlet />;
}Protecting Multiple Routes at Once
Group all protected routes under a single PrivateRoute wrapper so you don't repeat the guard on each route individually.
const router = createBrowserRouter([
{ path: '/login', element: <Login /> },
{
element: <PrivateRoute />,
children: [
{ path: '/dashboard', element: <Dashboard /> },
{ path: '/profile', element: <Profile /> },
{ path: '/settings', element: <Settings /> },
],
},
]);Token Expiry Handling
Check token expiry inside the guard or a global interceptor and log out the user when the token expires, redirecting to login.
function isTokenValid(token) {
const payload = JSON.parse(atob(token.split('.')[1]));
return payload.exp * 1000 > Date.now();
}
function requireAuth() {
const token = localStorage.getItem('token');
if (!token || !isTokenValid(token)) throw redirect('/login');
return token;
}Refresh Token Flow
Before redirecting on 401, attempt a token refresh. Only redirect to login if the refresh also fails.
async function requireAuth() {
let token = localStorage.getItem('token');
if (!token || isExpired(token)) {
token = await refreshToken();
if (!token) throw redirect('/login');
localStorage.setItem('token', token);
}
return token;
}Persisting Auth Across Reloads
Store the auth token in localStorage or an HttpOnly cookie. On app load, rehydrate the auth state before rendering protected routes.
Quick Check
When using a loader-based auth guard in React Router, what should you throw to redirect an unauthenticated user?
Recap
Protect routes with a PrivateRoute wrapper using <Outlet /> or a loader that throws redirect(). Preserve the intended URL in navigation state, handle loading states to avoid flashes, and group protected routes to avoid duplication.
Frequently asked questions
Is the “Protected Routes & Auth Guards” lesson free?
Yes — the full text of “Protected Routes & Auth Guards” is free to read here on the web, and the React Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the React Academy course, upgrade to CoddyKit PRO.
What will I learn in “Protected Routes & Auth Guards”?
Redirect unauthenticated users to login using wrapper components or loader-based guards. You practise React Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start React Academy?
No prior experience is required. React Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Protected Routes & Auth Guards” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this React Academy lesson?
Yes. Every React Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Nested Routes & Outlet Layouts
- Loaders & Actions (Data Router API)
- Protected Routes & Auth Guards
- Managing State in URL Search Params