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: checkAuthLoader to 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 null when access is allowed (returning undefined throws 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 localStorage or 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

React
// util/auth.ts (from Entry 4)export function checkAuthLoader() {  const token = getAuthToken();  if (!token || token === "EXPIRED") return redirect("/auth?mode=login");  return null;}
Angular
// 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 checkAuthLoader redirects.
  • Angular's canMatch can prevent lazy-loaded code from downloading; React Router has no direct equivalent.

Quick reference

TaskReactAngular
Protect one routeloader: checkAuthLoadercanActivate: [authGuard]
Protect a groupPathless parent with the loadercanActivateChild / canMatch
Deny accessreturn redirect('/auth?mode=login')return router.createUrlTree([...])
Allow accessreturn nullreturn true