> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/angular/angular/llms.txt
> Use this file to discover all available pages before exploring further.

# Route Guards

> Protect routes and control navigation using Angular route guards

Route guards are functions or classes that the Angular router executes to determine whether a navigation should be allowed or denied. Guards can protect routes from unauthorized access, prevent unsaved changes from being lost, and fetch data before activating routes.

## Types of Guards

Angular provides several types of guards for different scenarios:

| Guard Type         | Purpose                                   | When It Runs                  |
| ------------------ | ----------------------------------------- | ----------------------------- |
| `CanActivate`      | Controls if a route can be activated      | Before route activation       |
| `CanActivateChild` | Controls if child routes can be activated | Before child route activation |
| `CanDeactivate`    | Controls if a route can be deactivated    | Before leaving current route  |
| `CanMatch`         | Controls if a route can match the URL     | During route matching phase   |
| `Resolve`          | Pre-fetches data before activating route  | Before route activation       |

<Info>
  Guard interfaces are defined in `packages/router/src/models.ts` starting at line 857. The router executes guards using the logic in `packages/router/src/operators/check_guards.ts`.
</Info>

## Functional Guards (Recommended)

Modern Angular uses functional guards with the `inject()` function for dependency injection:

### CanActivate Guard

Controls whether a route can be activated:

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

export const authGuard: CanActivateFn = (route, state) => {
  const authService = inject(AuthService);
  const router = inject(Router);
  
  if (authService.isLoggedIn()) {
    return true;
  }
  
  // Redirect to login page
  return router.createUrlTree(['/login'], {
    queryParams: { returnUrl: state.url }
  });
};
```

```typescript routes.ts theme={null}
import { authGuard } from './guards/auth.guard';

export const routes: Routes = [
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivate: [authGuard] // Protect this route
  },
  {
    path: 'profile',
    component: ProfileComponent,
    canActivate: [authGuard] // Multiple guards can be applied
  }
];
```

### Multiple Guards

You can apply multiple guards to a single route:

```typescript admin.guard.ts theme={null}
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from './auth.service';

export const adminGuard: CanActivateFn = (route, state) => {
  const authService = inject(AuthService);
  const router = inject(Router);
  
  if (authService.hasRole('admin')) {
    return true;
  }
  
  // Redirect to access denied page
  return router.createUrlTree(['/access-denied']);
};
```

```typescript routes.ts theme={null}
export const routes: Routes = [
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [authGuard, adminGuard] // Both must pass
  }
];
```

<Note>
  Guards execute in order. If any guard returns `false` or redirects, subsequent guards won't execute.
</Note>

### CanActivateChild Guard

Protects child routes:

```typescript parent-auth.guard.ts theme={null}
import { inject } from '@angular/core';
import { CanActivateChildFn } from '@angular/router';
import { AuthService } from './auth.service';

export const parentAuthGuard: CanActivateChildFn = (childRoute, state) => {
  const authService = inject(AuthService);
  
  // Check permissions for child routes
  const requiredPermission = childRoute.data?.['permission'];
  
  if (requiredPermission) {
    return authService.hasPermission(requiredPermission);
  }
  
  return true;
};
```

```typescript routes.ts theme={null}
export const routes: Routes = [
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivateChild: [parentAuthGuard], // Applies to all children
    children: [
      { 
        path: 'reports', 
        component: ReportsComponent,
        data: { permission: 'view-reports' }
      },
      { 
        path: 'settings', 
        component: SettingsComponent,
        data: { permission: 'edit-settings' }
      }
    ]
  }
];
```

### CanDeactivate Guard

Prevents users from leaving a route with unsaved changes:

```typescript unsaved-changes.guard.ts theme={null}
import { CanDeactivateFn } from '@angular/router';

export interface CanComponentDeactivate {
  canDeactivate: () => boolean | Promise<boolean>;
}

export const unsavedChangesGuard: CanDeactivateFn<CanComponentDeactivate> = 
  (component, currentRoute, currentState, nextState) => {
    // If component has unsaved changes, ask for confirmation
    if (component.canDeactivate) {
      return component.canDeactivate();
    }
    return true;
  };
```

```typescript form.component.ts theme={null}
import { Component } from '@angular/core';
import { CanComponentDeactivate } from './guards/unsaved-changes.guard';

@Component({
  selector: 'app-form',
  template: `
    <form (ngSubmit)="save()">
      <input [(ngModel)]="data" name="data">
      <button type="submit">Save</button>
    </form>
  `
})
export class FormComponent implements CanComponentDeactivate {
  data = '';
  private savedData = '';

  canDeactivate(): boolean {
    // Check if there are unsaved changes
    if (this.data !== this.savedData) {
      return window.confirm('You have unsaved changes. Do you want to leave?');
    }
    return true;
  }

  save() {
    this.savedData = this.data;
    // Save logic...
  }
}
```

```typescript routes.ts theme={null}
import { unsavedChangesGuard } from './guards/unsaved-changes.guard';

export const routes: Routes = [
  {
    path: 'edit/:id',
    component: FormComponent,
    canDeactivate: [unsavedChangesGuard]
  }
];
```

### CanMatch Guard

Determines if a route configuration can be used:

```typescript feature-toggle.guard.ts theme={null}
import { inject } from '@angular/core';
import { CanMatchFn } from '@angular/router';
import { FeatureService } from './feature.service';

export const featureToggleGuard: CanMatchFn = (route, segments) => {
  const featureService = inject(FeatureService);
  const featureName = route.data?.['feature'];
  
  if (featureName) {
    return featureService.isEnabled(featureName);
  }
  
  return true;
};
```

```typescript routes.ts theme={null}
export const routes: Routes = [
  {
    path: 'beta-feature',
    component: BetaFeatureComponent,
    canMatch: [featureToggleGuard],
    data: { feature: 'beta-feature' }
  },
  {
    // Fallback route when feature is disabled
    path: 'beta-feature',
    component: ComingSoonComponent
  }
];
```

<Info>
  `CanMatch` guards are useful for A/B testing, feature flags, and conditional route loading. Unlike `CanActivate`, they prevent the route from being recognized at all.
</Info>

## Async Guards

Guards can return Observables or Promises for async operations:

```typescript permissions.guard.ts theme={null}
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { PermissionsService } from './permissions.service';
import { map } from 'rxjs/operators';

export const permissionsGuard: CanActivateFn = (route, state) => {
  const permissionsService = inject(PermissionsService);
  const router = inject(Router);
  const requiredPermission = route.data?.['permission'];
  
  // Return Observable<boolean>
  return permissionsService.hasPermission(requiredPermission).pipe(
    map(hasPermission => {
      if (hasPermission) {
        return true;
      }
      return router.createUrlTree(['/access-denied']);
    })
  );
};
```

```typescript async-auth.guard.ts theme={null}
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from './auth.service';

export const asyncAuthGuard: CanActivateFn = async (route, state) => {
  const authService = inject(AuthService);
  const router = inject(Router);
  
  try {
    // Return Promise<boolean>
    const isAuthenticated = await authService.checkAuthentication();
    
    if (isAuthenticated) {
      return true;
    }
    
    return router.createUrlTree(['/login']);
  } catch (error) {
    console.error('Authentication check failed:', error);
    return router.createUrlTree(['/error']);
  }
};
```

## RedirectCommand

Use `RedirectCommand` for more control over redirects:

```typescript redirect.guard.ts theme={null}
import { inject } from '@angular/core';
import { CanActivateFn, Router, RedirectCommand } from '@angular/router';
import { AuthService } from './auth.service';

export const redirectGuard: CanActivateFn = (route, state) => {
  const router = inject(Router);
  const authService = inject(AuthService);
  
  if (!authService.isLoggedIn()) {
    const loginUrl = router.parseUrl('/login');
    
    // RedirectCommand provides more control
    return new RedirectCommand(loginUrl, {
      skipLocationChange: true, // Don't update browser URL
      replaceUrl: true, // Replace current history entry
      state: { returnUrl: state.url } // Pass state to target route
    });
  }
  
  return true;
};
```

<Info>
  `RedirectCommand` is defined in `packages/router/src/models.ts:118` and provides fine-grained control over navigation behavior during redirects.
</Info>

## Data Resolvers

Resolvers fetch data before a route activates:

```typescript user.resolver.ts theme={null}
import { inject } from '@angular/core';
import { ResolveFn, Router, RedirectCommand } from '@angular/router';
import { UserService } from './user.service';
import { User } from './user.model';

export const userResolver: ResolveFn<User> = (route, state) => {
  const userService = inject(UserService);
  const router = inject(Router);
  const userId = route.paramMap.get('id');
  
  if (!userId) {
    // Redirect if no ID provided
    return new RedirectCommand(router.parseUrl('/users'));
  }
  
  // Fetch user data
  return userService.getUser(userId);
};
```

```typescript routes.ts theme={null}
import { userResolver } from './resolvers/user.resolver';

export const routes: Routes = [
  {
    path: 'user/:id',
    component: UserDetailComponent,
    resolve: {
      user: userResolver // Data available before component loads
    }
  }
];
```

```typescript user-detail.component.ts theme={null}
import { Component, OnInit, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { User } from './user.model';

@Component({
  selector: 'app-user-detail',
  template: `
    <h2>{{ user.name }}</h2>
    <p>{{ user.email }}</p>
  `
})
export class UserDetailComponent implements OnInit {
  private route = inject(ActivatedRoute);
  user!: User;

  ngOnInit() {
    // Data is already resolved
    this.route.data.subscribe(data => {
      this.user = data['user'];
    });
  }
}
```

### With Component Input Binding

```typescript user-detail.component.ts theme={null}
import { Component, Input } from '@angular/core';
import { User } from './user.model';

@Component({
  selector: 'app-user-detail',
  template: `
    <h2>{{ user.name }}</h2>
    <p>{{ user.email }}</p>
  `
})
export class UserDetailComponent {
  @Input() user!: User; // Automatically injected from resolver
}
```

## Class-Based Guards (Legacy)

While functional guards are recommended, class-based guards are still supported:

```typescript auth.guard.ts theme={null}
import { Injectable } from '@angular/core';
import { 
  CanActivate, 
  ActivatedRouteSnapshot, 
  RouterStateSnapshot,
  Router 
} from '@angular/router';
import { AuthService } from './auth.service';

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

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): boolean {
    if (this.authService.isLoggedIn()) {
      return true;
    }
    
    this.router.navigate(['/login'], {
      queryParams: { returnUrl: state.url }
    });
    return false;
  }
}
```

<Warning>
  Class-based guards are deprecated. Use functional guards with `inject()` for new code. They're more concise and tree-shakeable.
</Warning>

## Guard Execution Order

When multiple guards are present, they execute in this order:

1. **canDeactivate** - Current route's deactivation guards
2. **canMatch** - Route matching guards
3. **canLoad** - Lazy loading guards (deprecated, use canMatch)
4. **canActivateChild** - Parent route's child activation guards
5. **canActivate** - Route activation guards
6. **resolve** - Data resolvers

```typescript routes.ts theme={null}
export const routes: Routes = [
  {
    path: 'protected',
    canMatch: [featureToggleGuard], // 1st
    canActivate: [authGuard, adminGuard], // 2nd & 3rd
    canActivateChild: [parentAuthGuard], // Applies to children
    component: ProtectedComponent,
    resolve: {
      data: dataResolver // Last, only if guards pass
    },
    children: [
      {
        path: 'child',
        component: ChildComponent,
        canDeactivate: [unsavedChangesGuard] // When leaving
      }
    ]
  }
];
```

## Real-World Example

Comprehensive guard setup for an enterprise application:

```typescript app.routes.ts theme={null}
import { Routes } from '@angular/router';
import { authGuard } from './guards/auth.guard';
import { roleGuard } from './guards/role.guard';
import { unsavedChangesGuard } from './guards/unsaved-changes.guard';
import { featureToggleGuard } from './guards/feature-toggle.guard';
import { userResolver } from './resolvers/user.resolver';

export const routes: Routes = [
  // Public routes
  { path: 'login', component: LoginComponent },
  { path: 'home', component: HomeComponent },
  
  // Protected routes
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivate: [authGuard],
    children: [
      {
        path: 'profile',
        component: ProfileComponent,
        resolve: { user: userResolver }
      },
      {
        path: 'settings',
        component: SettingsComponent,
        canDeactivate: [unsavedChangesGuard]
      }
    ]
  },
  
  // Admin routes
  {
    path: 'admin',
    canActivate: [authGuard, roleGuard],
    canActivateChild: [roleGuard],
    data: { roles: ['admin', 'superadmin'] },
    children: [
      { path: 'users', component: AdminUsersComponent },
      { path: 'reports', component: AdminReportsComponent }
    ]
  },
  
  // Feature flagged route
  {
    path: 'beta',
    canMatch: [featureToggleGuard],
    data: { feature: 'beta-feature' },
    component: BetaFeatureComponent
  }
];
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Use Functional Guards" icon="code">
    Prefer functional guards with `inject()` over class-based guards for better tree-shaking and simplicity.
  </Card>

  <Card title="Return UrlTree for Redirects" icon="route">
    Return a `UrlTree` from guards instead of calling `router.navigate()` to let the router handle the redirect.
  </Card>

  <Card title="Handle Errors Gracefully" icon="triangle-exclamation">
    Always handle errors in async guards and provide fallback behavior.
  </Card>

  <Card title="Keep Guards Focused" icon="bullseye">
    Each guard should have a single responsibility. Combine multiple guards instead of creating complex logic in one.
  </Card>
</CardGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Lazy Loading" icon="download" href="/routing/lazy-loading">
    Learn how to implement lazy loading to optimize your application
  </Card>

  <Card title="Router Overview" icon="map" href="/routing/router-overview">
    Review the fundamentals of Angular routing
  </Card>
</CardGroup>
