100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
TypeScript

Route Guards

Learn how functional route guards control navigation access — protecting routes behind authentication, confirming unsaved changes, and pre-fetching data before activation.

Routing with the Angular RouterIntermediate10 min readJul 9, 2026
Analogies

Route Guards

Route guards are functions that the Angular Router runs at specific points in the navigation lifecycle to decide whether a navigation should proceed, be redirected, or be blocked entirely. Common use cases include restricting access to authenticated users only (canActivate), confirming that a user really wants to leave a page with unsaved form changes (canDeactivate), and pre-loading data before a component even renders (resolve). Since Angular 14+, guards are written as plain functions (functional guards) rather than injectable classes implementing interfaces like CanActivate, which reduces boilerplate significantly and is now the recommended style for Angular 17+ applications.

🏏

Cricket analogy: canActivate is like a stadium turnstile checking a valid ticket before letting a fan in, canDeactivate is like security confirming you really want to leave during a super over, and resolve is like fetching the pitch report before you take your seat.

canActivate — Protecting Routes

A canActivate guard runs before a route is activated and determines whether navigation is allowed to proceed. It can return a boolean, a UrlTree (to redirect elsewhere), or an Observable/Promise resolving to either, which lets you perform asynchronous checks like verifying a session token with a backend. Functional guards use the inject() function to access services, since they run outside a component's constructor context.

🏏

Cricket analogy: A canActivate guard is like the third umpire reviewing a run-out before confirming the decision stands, returning either 'not out' (allow), a redirect to the replay room (UrlTree), or waiting on a slow-motion feed (Observable) using inject() to pull in the review system.

typescript
// auth.guard.ts
import { inject } from '@angular/core';
import { Router, type CanActivateFn } from '@angular/router';
import { AuthService } from './auth.service';

export const authGuard: CanActivateFn = (route, state) => {
  const auth = inject(AuthService);
  const router = inject(Router);

  if (auth.isLoggedIn()) {
    return true;
  }

  // Redirect to login, preserving the attempted URL for a post-login redirect
  return router.createUrlTree(['/login'], {
    queryParams: { returnUrl: state.url },
  });
};

// app.routes.ts
export const routes: Routes = [
  { path: 'dashboard', component: DashboardComponent, canActivate: [authGuard] },
];

canDeactivate and Other Guard Types

canDeactivate runs when navigating away from a route and is commonly used to warn users about unsaved changes, typically by prompting a confirm dialog before allowing the navigation to continue. canMatch (which superseded the older canLoad) determines whether a route definition is even considered a match candidate at all — useful for feature-flagging entire route branches or choosing between two components for the same path based on a condition. A resolve function isn't strictly a guard but runs alongside guards to fetch data before the route activates, so the component never renders in a 'loading' state for that data.

🏏

Cricket analogy: canDeactivate is like an umpire confirming you really want to retire hurt with an unresolved review pending, canMatch is like a franchise choosing between two overseas players for the same slot based on visa eligibility, and resolve pre-fetches the pitch report first.

typescript
import type { CanDeactivateFn } from '@angular/router';
import type { EditFormComponent } from './edit-form.component';

export const unsavedChangesGuard: CanDeactivateFn<EditFormComponent> = (component) => {
  if (component.hasUnsavedChanges()) {
    return confirm('You have unsaved changes. Leave anyway?');
  }
  return true;
};

Because functional guards are plain functions, they compose naturally with utilities like Angular's own combineGuards-style patterns or simple helper functions — you can write a reusable hasRoleGuard(role: string) factory that returns a CanActivateFn, something that was much more awkward with class-based guards.

Route guards are a UX and routing convenience, not a security boundary. A canActivate guard only prevents the Angular Router from rendering a component in the browser — the actual protected data must still be secured server-side (e.g. via authenticated API endpoints), since a determined user can call the backend directly, bypassing the frontend guard entirely.

  • Since Angular 14+, guards are written as plain functions (CanActivateFn, CanDeactivateFn, etc.) using inject(), not injectable classes.
  • canActivate decides whether a route may be entered; returning a UrlTree performs a redirect.
  • canDeactivate runs on navigating away, commonly used for unsaved-changes confirmation prompts.
  • canMatch decides whether a route definition is even a candidate match, useful for feature flags.
  • resolve pre-fetches data before route activation so the component mounts with data already available.
  • Guards are a client-side UX mechanism, not a substitute for server-side authorization checks.

Practice what you learned

Was this page helpful?

Topics covered

#TypeScript#AngularStudyNotes#WebDevelopment#RouteGuards#Route#Guards#CanActivate#Protecting#StudyNotes#SkillVeris