Site icon Ampersand Tutorials

Angular Routing and Route Guards (2026)

Quick answer: Angular routing maps a URL to a component. You declare routes in app.routes.ts, render them with <router-outlet />, and navigate with routerLink or the Router service. Read URL data through ActivatedRoute, lazy-load large features with loadComponent, and protect routes with functional guards such as CanActivateFn.The sections below follow Angular routing in the order you build it: declaring routes, rendering them, reading parameters, lazy loading, then guards and navigation events, where most Angular routing mistakes appear.

Angular routing: declare the routes

// app.routes.ts
import { Routes } from '@angular/router';

export const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'products', component: ProductListComponent },
  { path: 'products/:id', component: ProductDetailComponent },
  { path: '**', component: NotFoundComponent },
];

Order matters. The wildcard ** route must come last, because the router takes the first match. The two products routes are distinct: the second expects a segment such as /products/42.

Render and navigate

Place one <router-outlet /> in the root template. The router swaps the active component into that position as the URL changes.

<nav>
  <a routerLink="/" routerLinkActive="active">Home</a>
  <a routerLink="/products" routerLinkActive="active">Products</a>
</nav>

<router-outlet />

Use routerLink rather than href. It navigates without a full page reload, and routerLinkActive adds a class to the link for the current route. For programmatic navigation, inject the router and call router.navigate(['/products', id]).

Reading parameters

Route parameters arrive as an observable or, more simply today, as a signal through input binding. Angular 16+ lets a component declare its route parameter as a normal signal input.

import { Component, inject, input, effect } from '@angular/core';
import { ProductService } from './product.service';

@Component({ selector: 'app-product-detail', template: `...` })
export class ProductDetailComponent {
  id = input.required<string>();     // bound from :id
  private service = inject(ProductService);

  constructor() {
    effect(() => this.service.load(this.id()));
  }
}

For this to work, enable it with withComponentInputBinding() in the router provider. If you prefer the classic approach, inject ActivatedRoute and read route.snapshot.paramMap.get('id') for a one-time value, or route.paramMap as an observable when the same component stays mounted across parameter changes.

Lazy loading a feature

Every route loaded at startup grows the initial bundle. Point a route at a component with loadComponent, and the router downloads it only when the user visits.

export const routes: Routes = [
  {
    path: 'admin',
    loadComponent: () =>
      import('./admin/admin.component').then(m => m.AdminComponent),
  },
];

For a whole feature with its own child routes, use loadChildren and a routes file. Lazy loading is the single highest-impact performance change in a growing app, and it costs one line per route.

Protecting routes with guards

A guard is a function that returns true to allow navigation or a UrlTree to redirect. Class-based guard interfaces such as CanActivate are deprecated in favour of functional guards.

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

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

  return auth.isLoggedIn() ? true : router.createUrlTree(['/login']);
};

Attach it to a route with canActivate: [authGuard]. The other guards you will meet are CanDeactivateFn — for warning about unsaved changes — and ResolveFn, which fetches data before the route activates so the component can render immediately.

Reacting to Angular routing events

The router emits events during navigation. Subscribe when you need to log page views or update a progress bar.

import { Router, NavigationEnd } from '@angular/router';

router.events.subscribe(event => {
  if (event instanceof NavigationEnd) {
    analytics.page(event.urlAfterRedirects);
  }
});

Continue with Angular forms. To learn routing and guards with a real multi-page app, Ampersand Academy teaches Angular one-to-one.

Frequently asked questions

Why must the wildcard route come last?

The router uses the first matching route. A wildcard ** route placed anywhere but last would catch every URL before the real routes are checked.

What is the difference between routerLink and href?

routerLink navigates through the Angular router without reloading the page. href performs a full browser navigation that discards the single-page app state.

How do I lazy load a route in Angular?

Use loadComponent for a single component or loadChildren for a feature with its own routes. The router downloads the code only when the user visits that route.

What is a functional route guard?

A guard written as a function that returns true to allow navigation or a UrlTree to redirect. It replaces the older class based CanActivate interface, which is deprecated.

How do I read a route parameter in Angular?

Bind it to a signal input with component input binding enabled, or inject ActivatedRoute and read route.snapshot.paramMap for a one-time value.

Exit mobile version