Adding Authentication to React AppsEntry 8 of 11
Protecting Routes
A loader that checks for a token and redirects to the login page if there isn't one. It's React Router's closest equivalent to an Angular route guard.
Notes
- Add
loader: checkAuthLoaderto every route that requires login (edit event, new event). - The loader runs before the page renders, so unauthorized users never see the page.
- A loader must return something. Return
nullwhen access is allowed (returningundefinedthrows an error in v6). - If the route already has a data loader, call
checkAuthLoader()at its start, or wrap protected routes in a pathless parent route with the check. - Client-side protection is UX only: a user can edit
localStorageor call the API directly. The backend must verify the token on every protected endpoint.
What / Why / When
- What
Route-level access control.
- Why
Logged-out users are sent to login instead of seeing pages that would fail.
- When
Every page that requires authentication.
Code compare
// util/auth.ts (from Entry 4)export function checkAuthLoader() { const token = getAuthToken(); if (!token || token === "EXPIRED") return redirect("/auth?mode=login"); return null;}// guards/auth.guard.tsimport { inject } from "@angular/core";import { CanActivateFn, Router } from "@angular/router";export const authGuard: CanActivateFn = () => { const authService = inject(AuthService); const router = inject(Router); return authService.isAuthenticated() ? true : router.createUrlTree(["/auth"], { queryParams: { mode: "login" } });};Differences and gotchas
- React has one mechanism (loaders) for both data and access checks. Angular separates them: guards for access, resolvers for data.
- Angular guards run before resolvers, so a blocked user never triggers the data fetch. In React, nested loaders run in parallel, so a parent data loader may still fetch while a child's
checkAuthLoaderredirects. - Angular's
canMatchcan prevent lazy-loaded code from downloading; React Router has no direct equivalent.
Quick reference
| Task | React | Angular |
|---|---|---|
| Protect one route | loader: checkAuthLoader | canActivate: [authGuard] |
| Protect a group | Pathless parent with the loader | canActivateChild / canMatch |
| Deny access | return redirect('/auth?mode=login') | return router.createUrlTree([...]) |
| Allow access | return null | return true |