← Back to list

Angular 21 Guards — A GoTo Guide You Wish You Had

If you have ever built an Angular app, you probably asked yourself at some point:

Rahul Shaw · 2025-11-30 16:11 · 52 claps · 4.1 min read
#angular-guards #angular-21-guards #angular-14-guards
Open on Medium ↗
Wiki topics: 🌐 · Web Development

Angular Guards

Angular Guards

Angular 21 Guards — A GoTo Guide You Wish You Had

If you have ever built an Angular app, you probably asked yourself at some point:

“How do I stop users from opening pages they’re not supposed to see?”

Maybe a user is not logged in. Maybe their subscription expired. Maybe you want to run a quick check before letting them inside a route.

For all these situations, Angular uses something powerful called guards.

What Are Guards?

Think of guards like security checkpoints in your app.

When a user tries to open a page (route), a guard decides:

  • Allow them in
  • Block them
  • Redirect them

Just like a security guard outside a building checks ID cards, Angular guards check your app’s conditions before navigation.

Why Do We Use Guards?

We use guards to control who can access which page or whether the user can leave a page.

  • Only logged-in users can open the dashboard
  • Only admins can open the admin panel
  • Users must complete payment before accessing premium pages
  • Prevent losing unsaved data when leaving a form page

Guards make your app safer, cleaner, and more consistent.

When Should You Use Guards?

Use a guard whenever you want to:

  • Protect a route before entry
  • Protect child routes under a parent
  • Prevent navigation away under certain conditions (like unsaved forms)
  • Dynamically decide whether a route configuration should match (for A/B features, feature flags, or module-level control)

Guards are perfect for login systems, onboarding flows, admin panels, unsaved-form warnings, feature toggles, and more.

How to Create Angular Guards.

To create a guard, using Angular CLI:

ng generate guard <guard-name>

Where Do We Use Guards?

You apply guards inside your route configuration, usually in the routes (or Routes) array.

{
  path: 'dashboard',
  canActivate: [authGuard],
  loadComponent: () => import('./dashboard').then(c => c.Dashboard)
}

the guard decides if this route should open or not.

How Guards Worked Before version 15

(Class-based Style was used till version 14)

Earlier versions of Angular used class-based guards (interfaces like CanActivate, CanDeactivate, etc.).

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
  constructor(private auth: AuthService, private router: Router) {}

  canActivate(): boolean {
    if (!this.auth.isLoggedIn()) {
      this.router.navigate(['/']);
      return false;
    }
    return true;
  }
}

Similar class-based code existed for other guards like CanDeactivate, CanActivateChild, CanMatch/CanLoad, etc. The pattern involves creating a class, implementing the appropriate interface, injecting services via constructor, and returning true/false (or other types) from the guard method.

This worked — but it had drawbacks:

  • Too much boilerplate
  • Need to create classes for every guard
  • Less intuitive and more verbose

How Guards Work in Angular after version 14

(Function-Based Style)

Angular now uses function-based guards — no classes, no interface-implementing boilerplate, just simple functions using inject() when dependencies are needed.

All four guards can now be implemented as standalone functions:

  • *CanActivateFn*
  • *CanActivateChildFn*
  • *CanDeactivateFn<T>*
  • *CanMatchFn*

1.CanActivate — Decide if route can be activated Use this to protect the route based on certain conditions.

import { CanActivateFn, Router } from '@angular/router';
import { inject } from '@angular/core';
import { Auth } from './auth';

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

  if (!auth.isLoggedIn()) {
    router.navigate(['/login']); 
    return false;
  }
  return true;
};
{ 
  path: 'dashboard', 
  canActivate: [authGuard], 
  loadComponent: () => import('./dashboard').then(c => c.Dashboard) 
}

This guard checks if the user is logged in. If not, they are redirected to the login page.

2.CanActivateChild — Guard for child routes Use this when you have nested/child routes and want to protect all of them under a parent.

import { CanActivateChildFn } from '@angular/router';
import { inject } from '@angular/core';
import { Auth } from './auth';

export const adminChildGuard: CanActivateChildFn = (childRoute, state) => {
  const auth = inject(Auth);
  return auth.userRole === 'admin';
};
{
  path: 'admin',
  canActivateChild: [adminChildGuard],
  children: [
    { path: 'users', loadComponent: () => import('./users').then(m => m.Users) },
    { path: 'settings', loadComponent: () => import('./settings').then(m => m.Settings) }
  ]
}

All child route under /admin will be protected by the same guard.

3.CanDeactivate — Decide if you can leave a route (navigate away) Useful to prevent losing unsaved data (like leaving a form page without saving).

import { CanDeactivateFn, Router } from '@angular/router';
import { inject } from '@angular/core';
import type { UserForm } from './user-form';

export const unsavedChangesGuard: CanDeactivateFn<UserForm> = (
  component,
  currentRoute,
  currentState,
  nextState
) => {
  if (component.hasUnsavedChanges()) {
    return confirm('You have unsaved changes. Do you really want to leave?');
  }
  return true;
};
{ 
  path: 'edit-profile', 
  canDeactivate: [unsavedChangesGuard],
  loadComponent: () => import('./userForm').then(m => m.UserForm),
}

When user tries to leave this route, the guard runs — and asks for confirmation if there are unsaved changes.

4.CanMatch — Decide if a route should match / load at all This is useful for feature toggles, conditional route loading, role-based route availability, etc.

import { CanMatchFn } from '@angular/router';
import { inject } from '@angular/core';
import { Feature } from './feature';

export const featureToggleGuard: CanMatchFn = (route, segments) => {
  const feature = inject(Feature);
  // Suppose we enable new dashboard only if feature flag is true
  return feature.isEnabled('newDashboard');
};
{ 
  path: 'new-dashboard', 
  canMatch: [featureToggleGuard], 
  loadComponent: () => import('./new-dashboard').then(c => c.NewDashboard) 
},
{ 
  path: 'dashboard', 
  loadComponent: () => import('./dashboard').then(c => c.Dashboard) 
}

If featureToggleGuard returns true, /new-dashboard route will match; otherwise router will skip it and maybe match fallback routes.

Real-World Use Cases — When to Use Which Guard

  • CanActivate: protect pages like dashboard, user profile — ensure user is logged in or has correct role
  • CanActivateChild: protect an entire section of nested routes — e.g. admin panel with many child pages
  • CanDeactivate: warn user when they try to leave a form page without saving changes
  • CanMatch: enable/disable certain routes based on feature flags, user roles, subscription status or A/B testing

Angular with latest release made guards lightweight, modern, and beginner-friendly. No more long classes. No more confusion. Just clean functions.

If you’re building apps that need login checks, admin restrictions, unsaved-form warnings, or feature-based route loading — guards will quickly become your best friends.

The new function-based style is not only easier — it also feels more “JavaScript-native” and matches the direction Angular is moving toward.


메타데이터
post_id
da5f85e98ac0
slug
angular-21-guards-a-goto-guide-you-wish-you-had-da5f85e98ac0
url
https://medium.com/@therahulkshaw/angular-21-guards-a-goto-guide-you-wish-you-had-da5f85e98ac0
canonical_url
https://medium.com/@therahulkshaw/angular-21-guards-a-goto-guide-you-wish-you-had-da5f85e98ac0
author_url
https://medium.com/@therahulkshaw
status
ok
fetched_at
2026-08-10 19:13:05