Angular Router
The Angular Router turns a single-page application into a multi-view experience by mapping URL paths to components, all without a full page reload.
Setting Up the Angular Router
In a modern standalone Angular application, routing is configured with provideRouter rather than importing RouterModule.forRoot into an NgModule. You define an array of Route objects — each mapping a path string to a component — and pass that array to provideRouter in your application's bootstrap configuration. The router then renders the matched component wherever you place a <router-outlet> in your template.
Defining Routes
A Route is a plain object with a path and a component (or, for lazy loading, a loadComponent function returning a dynamic import). Paths are matched in order, so more specific routes must come before general ones, and a wildcard route using '**' should always be listed last to catch anything unmatched.
Configuring routes for a standalone app
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter, RouterOutlet, Routes } from '@angular/router';
@Component({
selector: 'app-home',
standalone: true,
template: `
<h2>Home</h2>
<p>Welcome to the home page.</p>
`
})
export class HomeComponent {}
@Component({
selector: 'app-about',
standalone: true,
template: `
<h2>About</h2>
<p>This is the about page.</p>
`
})
export class AboutComponent {}
@Component({
selector: 'app-not-found',
standalone: true,
template: `
<h2>404</h2>
<p>Page not found.</p>
`
})
export class NotFoundComponent {}
export const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: '**', component: NotFoundComponent }
];
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
template: `<router-outlet></router-outlet>`
})
export class AppComponent {}
bootstrapApplication(AppComponent, {
providers: [provideRouter(routes)]
}).catch((err) => console.error(err));
Navigating with routerLink
Instead of plain <a href> tags, use the routerLink directive so navigation goes through the Angular Router without triggering a full page reload. Pair it with routerLinkActive to automatically apply a CSS class to the link matching the current URL, which is exactly what you want for highlighting the active tab in a navigation bar. For navigation triggered from code — after a form submits, for example — inject the Router service and call its navigate method.
A navigation bar with routerLink and router-outlet
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter, RouterLink, RouterLinkActive, RouterOutlet, Routes } from '@angular/router';
@Component({
selector: 'app-home',
standalone: true,
template: `<p>Home page content.</p>`
})
export class HomeComponent {}
@Component({
selector: 'app-about',
standalone: true,
template: `<p>About page content.</p>`
})
export class AboutComponent {}
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'about', component: AboutComponent }
];
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterLink, RouterLinkActive, RouterOutlet],
template: `
<nav>
<a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
Home
</a>
<a routerLink="/about" routerLinkActive="active">About</a>
</nav>
<router-outlet></router-outlet>
`
})
export class AppComponent {}
bootstrapApplication(AppComponent, {
providers: [provideRouter(routes)]
}).catch((err) => console.error(err));
- Route parameters — dynamic segments like :id captured from the URL
- Query parameters — optional key/value pairs appended after ?
- Route guards — functions that can allow, block, or redirect navigation
- Lazy loading — loadComponent or loadChildren to split code by route
- Nested routes — child <router-outlet> elements for layouts within layouts
Reading route parameters and navigating back
import { Component, inject } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { ActivatedRoute, provideRouter, Router, RouterOutlet, Routes } from '@angular/router';
import { toSignal } from '@angular/core/rxjs-interop';
import { map } from 'rxjs';
@Component({
selector: 'app-product-detail',
standalone: true,
template: `
<h2>Product #{{ productId() }}</h2>
<button (click)="goBack()">Back to list</button>
`
})
export class ProductDetailComponent {
private route = inject(ActivatedRoute);
private router = inject(Router);
productId = toSignal(
this.route.paramMap.pipe(map(params => params.get('id'))),
{ initialValue: null }
);
goBack(): void {
this.router.navigate(['/products']);
}
}
@Component({
selector: 'app-product-list',
standalone: true,
template: `<p>Product list placeholder. Try navigating to /products/42.</p>`
})
export class ProductListComponent {}
export const routes: Routes = [
{ path: 'products', component: ProductListComponent },
{ path: 'products/:id', component: ProductDetailComponent },
{ path: '', redirectTo: 'products/42', pathMatch: 'full' }
];
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
template: `<router-outlet></router-outlet>`
})
export class AppComponent {}
bootstrapApplication(AppComponent, {
providers: [provideRouter(routes)]
}).catch((err) => console.error(err));
Exercise: Angular Router
In a standalone app, how are routes typically registered?