What is Routing?
Routing maps URLs to components, enabling navigation between different views in your single-page application (SPA). Angular's built-in router handles:
- URL navigation without full page reloads
- Route parameters and query parameters
- Nested (child) routes
- Lazy loading of modules
- Route guards for access control
Setting Up the Router
Define Routes
typescript
// app.routes.ts
import { Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';
import { ContactComponent } from './contact/contact.component';
export const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: 'contact', component: ContactComponent },
{ path: '**', redirectTo: '' }, // Wildcard: redirect unknown routes to home
];Provide Router in App Config
typescript
// app.config.ts
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
],
};Add Router Outlet
The <router-outlet> is where routed components are rendered:
typescript
// app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, RouterLink, RouterLinkActive],
template: `
<nav>
<a routerLink="/" routerLinkActive="active"
[routerLinkActiveOptions]="{ exact: true }">Home</a>
<a routerLink="/about" routerLinkActive="active">About</a>
<a routerLink="/contact" routerLinkActive="active">Contact</a>
</nav>
<main>
<router-outlet />
</main>
`,
styles: [`.active { color: blue; font-weight: bold; }`],
})
export class AppComponent {}Navigation
Template Navigation with routerLink
html
<!-- Static links -->
<a routerLink="/products">Products</a>
<a routerLink="/products/42">Product #42</a>
<!-- Dynamic links using array syntax -->
<a [routerLink]="['/products', product.id]">{{ product.name }}</a>
<a [routerLink]="['/search']" [queryParams]="{ q: searchTerm }">
Search
</a>
<!-- With query parameters and fragments -->
<a routerLink="/docs"
[queryParams]="{ page: 2, sort: 'name' }"
fragment="section-1">
Docs Page 2
</a>
<!-- Results in: /docs?page=2&sort=name#section-1 -->Programmatic Navigation
typescript
import { Router } from '@angular/router';
@Component({ /* ... */ })
export class LoginComponent {
private router = inject(Router);
onLogin() {
// Simple navigation
this.router.navigate(['/dashboard']);
// With route params
this.router.navigate(['/users', userId]);
// With query params
this.router.navigate(['/search'], {
queryParams: { q: 'angular', page: 1 },
});
// Navigate by URL string
this.router.navigateByUrl('/dashboard/settings');
}
}Route Parameters
Define Route with Parameter
typescript
const routes: Routes = [
{ path: 'users', component: UserListComponent },
{ path: 'users/:id', component: UserDetailComponent },
{ path: 'products/:category/:id', component: ProductComponent },
];Read Route Parameters
typescript
import { Component, inject, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-user-detail',
standalone: true,
template: `
<h2>User #{{ userId }}</h2>
<!-- user details -->
`,
})
export class UserDetailComponent implements OnInit {
private route = inject(ActivatedRoute);
userId!: string;
ngOnInit() {
// Snapshot (gets the value once)
this.userId = this.route.snapshot.params['id'];
// Observable (reacts to changes when same component reused)
this.route.params.subscribe(params => {
this.userId = params['id'];
this.loadUser(this.userId);
});
}
private loadUser(id: string) {
// Fetch user data
}
}Using input() for Route Params (Angular 16+)
typescript
// Enable in app config
provideRouter(routes, withComponentInputBinding())
// Component receives route params as inputs automatically
@Component({ /* ... */ })
export class UserDetailComponent {
@Input() id!: string; // Automatically bound from :id route param
ngOnInit() {
console.log('User ID:', this.id);
}
}Query Parameters
typescript
// Navigate with query params
this.router.navigate(['/search'], {
queryParams: { q: 'angular', category: 'frameworks' },
});
// URL: /search?q=angular&category=frameworks
// Read query params
export class SearchComponent implements OnInit {
private route = inject(ActivatedRoute);
ngOnInit() {
// Snapshot
const query = this.route.snapshot.queryParams['q'];
// Observable
this.route.queryParams.subscribe(params => {
console.log('Search query:', params['q']);
console.log('Category:', params['category']);
});
}
}Child Routes (Nested)
Group related routes under a parent:
typescript
const routes: Routes = [
{
path: 'admin',
component: AdminLayoutComponent,
children: [
{ path: '', component: AdminDashboardComponent },
{ path: 'users', component: AdminUsersComponent },
{ path: 'users/:id', component: AdminUserEditComponent },
{ path: 'settings', component: AdminSettingsComponent },
],
},
];The parent component needs its own <router-outlet>:
typescript
@Component({
selector: 'app-admin-layout',
standalone: true,
imports: [RouterOutlet, RouterLink],
template: `
<div class="admin-layout">
<aside class="sidebar">
<a routerLink="/admin">Dashboard</a>
<a routerLink="/admin/users">Users</a>
<a routerLink="/admin/settings">Settings</a>
</aside>
<main class="content">
<router-outlet />
</main>
</div>
`,
})
export class AdminLayoutComponent {}Redirects and Wildcards
typescript
const routes: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'old-about', redirectTo: '/about' },
{ path: '**', component: NotFoundComponent }, // 404 page
];Important: The wildcard route (**) must be the last route โ Angular matches routes in order.
Route Data
Pass static data to a route:
typescript
const routes: Routes = [
{
path: 'dashboard',
component: DashboardComponent,
data: {
title: 'Dashboard',
requiresAuth: true,
},
},
];
// Read in component
export class DashboardComponent {
private route = inject(ActivatedRoute);
ngOnInit() {
const title = this.route.snapshot.data['title'];
document.title = title;
}
}Active Link Styling
html
<nav>
<a routerLink="/home"
routerLinkActive="active"
[routerLinkActiveOptions]="{ exact: true }">
Home
</a>
<a routerLink="/products" routerLinkActive="active">Products</a>
<a routerLink="/about" routerLinkActive="active">About</a>
</nav>css
.active {
color: #1976d2;
font-weight: 600;
border-bottom: 2px solid #1976d2;
}Next Steps
Routes need protection โ not every user should access every page. Next, we'll learn about route guards to control navigation and protect routes based on authentication, roles, and conditions.