> ## 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

> API reference for Angular route guards - protect and control navigation with guard functions and interfaces.

# Route Guards

Route guards are functions or classes that control navigation in Angular applications. They can prevent navigation, redirect to different routes, or allow navigation to proceed based on custom logic.

## Import

```typescript theme={null}
import {
  CanActivateFn,
  CanActivateChildFn,
  CanDeactivateFn,
  CanMatchFn,
  CanLoadFn,
  GuardResult
} from '@angular/router';
```

## Guard Types

Angular provides several types of guards for different navigation scenarios:

<CardGroup cols={2}>
  <Card title="CanActivate" icon="lock">
    Controls if a route can be activated
  </Card>

  <Card title="CanActivateChild" icon="shield">
    Controls if child routes can be activated
  </Card>

  <Card title="CanDeactivate" icon="door-open">
    Controls if a route can be deactivated
  </Card>

  <Card title="CanMatch" icon="filter">
    Controls if a route can be matched
  </Card>
</CardGroup>

<Note>
  Modern Angular applications use functional guards (functions) instead of class-based guards. Functional guards are more concise and leverage dependency injection through the `inject()` function.
</Note>

## Guard Result Types

### GuardResult

```typescript theme={null}
type GuardResult = boolean | UrlTree | RedirectCommand;
```

All guards return a `GuardResult`, which can be:

* `true` - Allow navigation to proceed
* `false` - Cancel navigation
* `UrlTree` - Redirect to a different URL
* `RedirectCommand` - Redirect with navigation options

### MaybeAsync

```typescript theme={null}
type MaybeAsync<T> = T | Observable<T> | Promise<T>;
```

Guards can return synchronous values or async values (Observable or Promise).

## CanActivate

### CanActivateFn

```typescript theme={null}
type CanActivateFn = (
  route: ActivatedRouteSnapshot,
  state: RouterStateSnapshot
) => MaybeAsync<GuardResult>;
```

Determines if a route can be activated. Used to protect routes that require authentication, authorization, or other preconditions.

**Parameters:**

<ParamField path="route" type="ActivatedRouteSnapshot" required>
  The activated route snapshot containing route parameters, data, and configuration.
</ParamField>

<ParamField path="state" type="RouterStateSnapshot" required>
  The router state snapshot containing the target URL and routing tree.
</ParamField>

**Returns:** `true` to allow, `false` to cancel, or `UrlTree`/`RedirectCommand` to redirect.

**Examples:**

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

// Basic authentication guard
export const authGuard: CanActivateFn = (route, state) => {
  const authService = inject(AuthService);
  const router = inject(Router);

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

  // Redirect to login page with return URL
  return router.createUrlTree(['/login'], {
    queryParams: { returnUrl: state.url }
  });
};

// Role-based authorization guard
export const adminGuard: CanActivateFn = (route, state) => {
  const authService = inject(AuthService);
  const router = inject(Router);

  if (authService.hasRole('admin')) {
    return true;
  }

  // Redirect to unauthorized page
  return router.createUrlTree(['/unauthorized']);
};

// Async guard with Observable
export const permissionGuard: CanActivateFn = (route, state) => {
  const authService = inject(AuthService);
  const requiredPermission = route.data['permission'];

  return authService.hasPermission(requiredPermission).pipe(
    map(hasPermission => hasPermission || router.createUrlTree(['/unauthorized']))
  );
};

// Guard with route parameter validation
export const userAccessGuard: CanActivateFn = (route, state) => {
  const authService = inject(AuthService);
  const userId = route.params['id'];
  const currentUserId = authService.getCurrentUserId();

  // Users can only access their own profile
  if (userId === currentUserId || authService.hasRole('admin')) {
    return true;
  }

  return false;
};
```

**Route Configuration:**

```typescript theme={null}
import { Routes } from '@angular/router';

const routes: Routes = [
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivate: [authGuard]
  },
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [authGuard, adminGuard] // Multiple guards
  },
  {
    path: 'users/:id',
    component: UserProfileComponent,
    canActivate: [authGuard, userAccessGuard],
    data: { permission: 'users:read' }
  }
];
```

### CanActivate Interface (Deprecated)

```typescript theme={null}
interface CanActivate {
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): MaybeAsync<GuardResult>;
}
```

<Warning>
  Class-based guards using `CanActivate` interface are deprecated. Use functional guards (`CanActivateFn`) instead.
</Warning>

## CanActivateChild

### CanActivateChildFn

```typescript theme={null}
type CanActivateChildFn = (
  childRoute: ActivatedRouteSnapshot,
  state: RouterStateSnapshot
) => MaybeAsync<GuardResult>;
```

Determines if child routes can be activated. Applied to parent routes to protect all child routes.

**Example:**

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

// Protect all child routes
export const adminChildGuard: CanActivateChildFn = (route, state) => {
  const authService = inject(AuthService);
  const router = inject(Router);

  if (authService.hasRole('admin')) {
    return true;
  }

  return router.createUrlTree(['/unauthorized']);
};

// Check specific permissions for child routes
export const moduleAccessGuard: CanActivateChildFn = (route, state) => {
  const authService = inject(AuthService);
  const requiredModule = route.data['module'];

  if (authService.hasModuleAccess(requiredModule)) {
    return true;
  }

  return false;
};
```

**Route Configuration:**

```typescript theme={null}
const routes: Routes = [
  {
    path: 'admin',
    component: AdminLayoutComponent,
    canActivateChild: [adminChildGuard], // Applied to all children
    children: [
      { path: 'users', component: UsersComponent },
      { path: 'settings', component: SettingsComponent },
      { path: 'reports', component: ReportsComponent }
    ]
  }
];
```

## CanDeactivate

### CanDeactivateFn

```typescript theme={null}
type CanDeactivateFn<T> = (
  component: T,
  currentRoute: ActivatedRouteSnapshot,
  currentState: RouterStateSnapshot,
  nextState: RouterStateSnapshot
) => MaybeAsync<GuardResult>;
```

Determines if a route can be deactivated. Commonly used to prevent navigation away from pages with unsaved changes.

**Parameters:**

<ParamField path="component" type="T" required>
  The component instance being deactivated.
</ParamField>

<ParamField path="currentRoute" type="ActivatedRouteSnapshot" required>
  The current activated route.
</ParamField>

<ParamField path="currentState" type="RouterStateSnapshot" required>
  The current router state.
</ParamField>

<ParamField path="nextState" type="RouterStateSnapshot" required>
  The target router state.
</ParamField>

**Examples:**

```typescript theme={null}
import { CanDeactivateFn } from '@angular/router';

// Define interface for components with unsaved changes
export interface CanComponentDeactivate {
  canDeactivate: () => boolean | Promise<boolean>;
}

// Guard for unsaved changes
export const unsavedChangesGuard: CanDeactivateFn<CanComponentDeactivate> = (
  component,
  currentRoute,
  currentState,
  nextState
) => {
  // If component implements canDeactivate, call it
  if (component.canDeactivate) {
    return component.canDeactivate();
  }
  return true;
};

// Guard with user confirmation dialog
export const confirmLeaveGuard: CanDeactivateFn<any> = (
  component,
  currentRoute,
  currentState,
  nextState
) => {
  if (component.hasUnsavedChanges?.()) {
    return confirm('You have unsaved changes. Do you really want to leave?');
  }
  return true;
};

// Async guard with custom dialog service
export const asyncConfirmGuard: CanDeactivateFn<any> = async (
  component,
  currentRoute,
  currentState,
  nextState
) => {
  if (!component.hasUnsavedChanges?.()) {
    return true;
  }

  const dialogService = inject(DialogService);
  const result = await dialogService.confirm({
    title: 'Unsaved Changes',
    message: 'You have unsaved changes. Do you want to discard them?',
    confirmText: 'Discard',
    cancelText: 'Stay'
  });

  return result;
};
```

**Component Implementation:**

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

@Component({
  selector: 'app-edit-form',
  template: `
    <form [formGroup]="form">
      <!-- form fields -->
    </form>
  `
})
export class EditFormComponent implements CanComponentDeactivate {
  form: FormGroup;
  private savedData: any;

  canDeactivate(): boolean {
    // Check if form has unsaved changes
    if (this.form.dirty) {
      return confirm('You have unsaved changes. Are you sure you want to leave?');
    }
    return true;
  }

  hasUnsavedChanges(): boolean {
    return this.form.dirty;
  }
}
```

**Route Configuration:**

```typescript theme={null}
const routes: Routes = [
  {
    path: 'edit',
    component: EditFormComponent,
    canDeactivate: [unsavedChangesGuard]
  }
];
```

## CanMatch

### CanMatchFn

```typescript theme={null}
type CanMatchFn = (
  route: Route,
  segments: UrlSegment[],
  currentSnapshot?: PartialMatchRouteSnapshot
) => MaybeAsync<GuardResult>;
```

Determines if a route configuration can be matched. Used for feature flags, conditional routing, and A/B testing.

**Parameters:**

<ParamField path="route" type="Route" required>
  The route configuration being evaluated.
</ParamField>

<ParamField path="segments" type="UrlSegment[]" required>
  The URL segments that have not been consumed yet.
</ParamField>

<ParamField path="currentSnapshot" type="PartialMatchRouteSnapshot">
  The current route snapshot up to this point in matching (optional for backward compatibility).
</ParamField>

**Examples:**

```typescript theme={null}
import { inject } from '@angular/core';
import { CanMatchFn } from '@angular/router';
import { FeatureFlagService } from './feature-flag.service';

// Feature flag guard
export const featureGuard: CanMatchFn = (route, segments) => {
  const featureFlags = inject(FeatureFlagService);
  const featureName = route.data?.['feature'];

  if (!featureName) {
    return true;
  }

  return featureFlags.isEnabled(featureName);
};

// Environment-based routing
export const productionOnlyGuard: CanMatchFn = () => {
  return environment.production;
};

// User segment guard for A/B testing
export const betaUserGuard: CanMatchFn = (route, segments) => {
  const userService = inject(UserService);
  return userService.isBetaUser();
};

// Mobile/Desktop conditional routing
export const mobileGuard: CanMatchFn = () => {
  const platformService = inject(PlatformService);
  return platformService.isMobile();
};
```

**Route Configuration:**

```typescript theme={null}
const routes: Routes = [
  // New feature behind feature flag
  {
    path: 'dashboard',
    component: NewDashboardComponent,
    canMatch: [featureGuard],
    data: { feature: 'new-dashboard' }
  },
  // Fallback to old dashboard if feature disabled
  {
    path: 'dashboard',
    component: OldDashboardComponent
  },

  // A/B testing routes
  {
    path: 'home',
    component: HomeVariantAComponent,
    canMatch: [betaUserGuard]
  },
  {
    path: 'home',
    component: HomeVariantBComponent
  },

  // Platform-specific routes
  {
    path: 'editor',
    component: MobileEditorComponent,
    canMatch: [mobileGuard]
  },
  {
    path: 'editor',
    component: DesktopEditorComponent
  }
];
```

<Note>
  Unlike `CanActivate`, `CanMatch` prevents the route from being matched at all. If a `canMatch` guard returns `false`, the router continues to the next route configuration. This is ideal for feature flags and conditional routing.
</Note>

## CanLoad (Deprecated)

### CanLoadFn

```typescript theme={null}
type CanLoadFn = (
  route: Route,
  segments: UrlSegment[]
) => MaybeAsync<GuardResult>;
```

<Warning>
  `CanLoad` is deprecated in favor of `CanMatch`. Use `CanMatchFn` for all new code.
</Warning>

## RedirectCommand

### RedirectCommand Class

```typescript theme={null}
class RedirectCommand {
  constructor(
    readonly redirectTo: UrlTree,
    readonly navigationBehaviorOptions?: NavigationBehaviorOptions
  );
}
```

Used in guards and resolvers to redirect with specific navigation options.

**Example:**

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

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

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

  const loginUrl = router.parseUrl('/login');
  return new RedirectCommand(loginUrl, {
    skipLocationChange: true, // Don't update browser URL
    state: { returnUrl: state.url } // Pass data to login page
  });
};
```

## Guard Execution Order

When multiple guards are present, they execute in the following order:

1. **Route matching**: `canMatch` guards
2. **Activation**: `canActivate` guards (from parent to child)
3. **Child activation**: `canActivateChild` guards (from parent to child)
4. **Deactivation**: `canDeactivate` guards (when leaving route)

**Example:**

```typescript theme={null}
const routes: Routes = [
  {
    path: 'admin',
    component: AdminLayoutComponent,
    canMatch: [featureGuard], // 1. Check feature flag
    canActivate: [authGuard], // 2. Check authentication
    canActivateChild: [adminGuard], // 3. Check admin role for children
    children: [
      {
        path: 'settings',
        component: SettingsComponent,
        canActivate: [settingsAccessGuard], // 4. Additional guard for this child
        canDeactivate: [unsavedChangesGuard] // 5. Check when leaving
      }
    ]
  }
];
```

## Common Guard Patterns

### Combining Multiple Guards

```typescript theme={null}
const routes: Routes = [
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [
      authGuard,      // Must be logged in
      adminGuard,     // Must be admin
      licenseGuard    // Must have valid license
    ]
  }
];
```

### Reusable Permission Guard

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

export const permissionGuard = (permission: string): CanActivateFn => {
  return (route, state) => {
    const authService = inject(AuthService);
    return authService.hasPermission(permission);
  };
};

// Usage
const routes: Routes = [
  {
    path: 'users',
    component: UsersComponent,
    canActivate: [permissionGuard('users:read')]
  },
  {
    path: 'users/create',
    component: CreateUserComponent,
    canActivate: [permissionGuard('users:create')]
  }
];
```

### Guard with Loading State

```typescript theme={null}
import { inject } from '@angular/core';
import { CanActivateFn } from '@angular/router';
import { LoadingService } from './loading.service';
import { AuthService } from './auth.service';
import { tap, finalize } from 'rxjs/operators';

export const authWithLoadingGuard: CanActivateFn = (route, state) => {
  const authService = inject(AuthService);
  const loadingService = inject(LoadingService);

  loadingService.show();

  return authService.checkAuth().pipe(
    tap(isAuthenticated => {
      if (!isAuthenticated) {
        // Handle redirect
      }
    }),
    finalize(() => loadingService.hide())
  );
};
```

### Guard with Error Handling

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

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

  return authService.checkAuth().pipe(
    catchError(error => {
      console.error('Auth check failed:', error);
      // Redirect to error page or login
      return of(router.createUrlTree(['/error']));
    })
  );
};
```

## Testing Guards

```typescript theme={null}
import { TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { authGuard } from './auth.guard';
import { AuthService } from './auth.service';

describe('authGuard', () => {
  let authService: jasmine.SpyObj<AuthService>;
  let router: jasmine.SpyObj<Router>;

  beforeEach(() => {
    const authServiceSpy = jasmine.createSpyObj('AuthService', ['isLoggedIn']);
    const routerSpy = jasmine.createSpyObj('Router', ['createUrlTree']);

    TestBed.configureTestingModule({
      providers: [
        { provide: AuthService, useValue: authServiceSpy },
        { provide: Router, useValue: routerSpy }
      ]
    });

    authService = TestBed.inject(AuthService) as jasmine.SpyObj<AuthService>;
    router = TestBed.inject(Router) as jasmine.SpyObj<Router>;
  });

  it('should allow access when user is logged in', () => {
    authService.isLoggedIn.and.returnValue(true);

    const result = TestBed.runInInjectionContext(() =>
      authGuard(null as any, null as any)
    );

    expect(result).toBe(true);
  });

  it('should redirect to login when user is not logged in', () => {
    authService.isLoggedIn.and.returnValue(false);
    const mockUrlTree = {} as any;
    router.createUrlTree.and.returnValue(mockUrlTree);

    const result = TestBed.runInInjectionContext(() =>
      authGuard(null as any, { url: '/admin' } as any)
    );

    expect(result).toBe(mockUrlTree);
    expect(router.createUrlTree).toHaveBeenCalledWith(
      ['/login'],
      { queryParams: { returnUrl: '/admin' } }
    );
  });
});
```

## See Also

<CardGroup cols={2}>
  <Card title="Router Class" icon="code" href="/api/router/router-class">
    Router service API
  </Card>

  <Card title="Route Configuration" icon="route" href="/api/router/route-config">
    Configure routes with guards
  </Card>

  <Card title="Route Guards Guide" icon="book" href="/routing/route-guards">
    Complete guide to route guards
  </Card>

  <Card title="Dependency Injection" icon="syringe" href="/concepts/dependency-injection">
    Using inject() in guards
  </Card>
</CardGroup>
