Angular Router
The Router maps URLs to views and lets users navigate your app.
Router Essentials
- URL-driven UI: The Router swaps views based on the URL.
RouterOutlet
: Placeholder where the active route's component renders.routerLink
: Navigate without full page reloads.RouterLinkActive
: Adds classes to active links (use{ exact: true }
for root).- Performance & control: Lazy load feature areas; use guards to allow/block navigation.
import { provideRouter, withHashLocation, RouterOutlet, RouterLink } from '@angular/router';
const routes = [
{ path: '', component: Home },
{ path: 'about', component: About }
];
bootstrapApplication(App, {
providers: [provideRouter(routes, withHashLocation())]
});
// Template
// <a routerLink="/about">About</a>
// <router-outlet></router-outlet>
Notes:
- Related: See Components for building views, Services for shared logic/guards, and Templates for markup and directives.
- Standalone apps: import
RouterOutlet
/RouterLink
and provide routes withprovideRouter()
. - Sandboxes/runners: use
withHashLocation()
so links work without server config (hash URLs look like/#/path
).
Router Basics
- Define a
routes
array that maps paths to components. - Provide routes with
provideRouter()
(usewithHashLocation()
for sandboxes). - Use
routerLink
for navigation andRouterOutlet
to render views.
const routes = [ { path: '', component: Home }, { path: 'about', component: About } ];
// template
// <a routerLink="/about">About</a>
// <router-outlet></router-outlet>
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component } from '@angular/core';
import { provideRouter, RouterOutlet, RouterLink, withHashLocation } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, RouterLink],
template: `
<h3>Router</h3>
<nav>
<a routerLink="/">Home</a> |
<a routerLink="/about">About</a>
</nav>
<router-outlet></router-outlet>
`
})
export class App {}
@Component({
standalone: true,
template: `<p>Home works!</p>`
})
export class Home {}
@Component({
standalone: true,
template: `<p>About works!</p>`
})
export class About {}
const routes = [
{ path: '', component: Home },
{ path: 'about', component: About }
];
bootstrapApplication(App, {
providers: [provideRouter(routes, withHashLocation())]
});
<app-root></app-root>
Example explained
provideRouter(routes)
: Registers the routes for the app.RouterOutlet
: Placeholder where the active route's component renders.routerLink
: Navigates without reloading the page.withHashLocation()
: Uses hash URLs so links work in sandboxes without server rewrites.
Notes:
- Use routerLink, not href:
href
reloads the page. - Use
routerLink
for SPA navigation. - Standalone imports: Import
RouterOutlet
/RouterLink
and provide routes withprovideRouter()
.
Router Params
- Capture variables in paths with
:id
(e.g.,/product/42
). - Read them via
ActivatedRoute
(snapshot
orparamMap
observable).
{ path: 'product/:id', component: Product }
// class Product {
// id = '';
// route = inject(ActivatedRoute);
// ngOnInit() { this.id = this.route.snapshot.paramMap.get('id') ?? ''; }
// }
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component, OnInit, inject } from '@angular/core';
import { provideRouter, RouterOutlet, RouterLink, RouterLinkActive, ActivatedRoute, withHashLocation } from '@angular/router';
@Component({
selector: 'home-cmp',
standalone: true,
template: `<p>Home works!</p>`
})
export class Home {}
@Component({
selector: 'product-cmp',
standalone: true,
template: `<p>Product ID: {{ id }}</p>`
})
export class Product implements OnInit {
id = '';
private route = inject(ActivatedRoute);
ngOnInit() {
this.id = this.route.snapshot.paramMap.get('id') ?? '';
}
}
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, RouterLink, RouterLinkActive],
template: `
<h3>Router Params</h3>
<nav>
<a routerLink="/">Home</a> |
<a routerLink="/product/1" routerLinkActive="active">Product 1</a> |
<a routerLink="/product/2" routerLinkActive="active">Product 2</a>
</nav>
<router-outlet></router-outlet>
`,
styles: [`nav a { margin-right: 6px; } .active { font-weight: bold; }`]
})
export class App {}
const routes = [
{ path: '', component: Home },
{ path: 'product/:id', component: Product }
];
bootstrapApplication(App, {
providers: [provideRouter(routes, withHashLocation())]
});
<app-root></app-root>
Example explained
product/:id
: Declares a path parameter namedid
.ActivatedRoute
: Readsid
fromsnapshot.paramMap.get('id')
inngOnInit
.- Links:
routerLink="/product/1"
and/product/2
demonstrate parameterized navigation.
Params update within the same component: If navigating to the same route with different params, subscribe to paramMap
(or params
) instead of using a one-time snapshot
.
Active Links
- Use
routerLinkActive
to toggle classes when a link matches. - Set
{ exact: true }
for root links like/
.
<a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">Home</a>
<a routerLink="/about" routerLinkActive="active">About</a>
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component } from '@angular/core';
import { provideRouter, RouterOutlet, RouterLink, RouterLinkActive, withHashLocation } from '@angular/router';
@Component({
standalone: true,
template: `<p>Home works!</p>`
})
export class Home {}
@Component({
standalone: true,
template: `<p>About works!</p>`
})
export class About {}
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, RouterLink, RouterLinkActive],
styles: [`
nav a { margin-right: 8px; text-decoration: none; }
.active { font-weight: 600; color: seagreen; }
`],
template: `
<h3>Active Links (routerLinkActive)</h3>
<nav>
<a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">Home</a>
<a routerLink="/about" routerLinkActive="active">About</a>
</nav>
<router-outlet></router-outlet>
`
})
export class App {}
const routes = [
{ path: '', component: Home },
{ path: 'about', component: About }
];
bootstrapApplication(App, {
providers: [provideRouter(routes, withHashLocation())]
});
<app-root></app-root>
Example explained
routerLinkActive="active"
: Adds the active class when the link matches the current URL.[routerLinkActiveOptions]="{ exact: true }"
: For the root (/
), only mark active on exact match.RouterOutlet
: Renders the matched component for the current route.
Notes:
- Exact for root: For
/
links, set{ exact: true }
so parent paths don't keep them active. - Apply on containers: Put
routerLinkActive
on a parent element to style groups of links. - Multiple classes: You can add several classes:
routerLinkActive="active bold"
.
Lazy-loaded Component
- Defer loading code until navigation with
loadComponent
orloadChildren
. - Improves initial load time by splitting bundles.
{ path: 'about', loadComponent: () => import('./about').then(m => m.About) }
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component } from '@angular/core';
import { provideRouter, RouterOutlet, RouterLink, withHashLocation } from '@angular/router';
@Component({
standalone: true,
template: `<p>Home works!</p>`
})
export class Home {}
@Component({
standalone: true,
template: `<p>About works (lazy)!</p>`
})
export class About {}
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, RouterLink],
styles: [`
nav a { margin-right: 8px; text-decoration: none; }
`],
template: `
<h3>Lazy-loaded Component (loadComponent)</h3>
<nav>
<a routerLink="/">Home</a>
<a routerLink="/about">About (lazy)</a>
</nav>
<router-outlet></router-outlet>
`
})
export class App {}
const routes = [
{ path: '', component: Home },
// Use Promise.resolve to simulate lazy loading without dynamic imports
{ path: 'about', loadComponent: () => Promise.resolve(About) }
];
bootstrapApplication(App, {
providers: [provideRouter(routes, withHashLocation())]
});
<app-root></app-root>
Example explained
loadComponent
: Defers loading the route's component until navigation (here simulated withPromise.resolve
).- Routes: Home is eager; About is lazy.
- Navigation: Clicking “About (lazy)” loads and renders the component on demand.
Notes:
- Route order and wildcards: Put catch-all routes last so they don't swallow other routes.
- Deep-link refreshes: Without server rewrites, refreshes may 404.
- In demos/sandboxes use
withHashLocation()
.
Route Guard (canActivate)
- Guards decide if navigation is allowed.
- Return
true
(allow),false
/UrlTree
(block/redirect), or async equivalents.
export const authGuard = () => isLoggedIn ? true : inject(Router).createUrlTree(['/']);
{ path: 'protected', component: Protected, canActivate: [authGuard] }
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component, inject } from '@angular/core';
import { provideRouter, RouterOutlet, RouterLink, withHashLocation, Router } from '@angular/router';
let loggedIn = false;
export const authGuard = () => {
if (loggedIn) return true;
const router = inject(Router);
return router.createUrlTree(['/']);
};
@Component({
standalone: true,
template: `<p>Home (public).</p>
<p>Login to access protected route.</p>`
})
export class Home {}
@Component({
standalone: true,
template: `<p>Protected works! You are logged in.</p>`
})
export class Protected {}
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, RouterLink],
styles: [`
nav a { margin-right: 8px; text-decoration: none; }
.status { margin-left: 8px; font-weight: 600; }
`],
template: `
<h3>Route Guard (canActivate)</h3>
<div class="toolbar">
<button (click)="toggle()">{{ loggedIn ? 'Log out' : 'Log in' }}</button>
<span class="status">Status: {{ loggedIn ? 'Logged in' : 'Logged out' }}</span>
</div>
<nav>
<a routerLink="/">Home</a>
<a routerLink="/protected">Protected</a>
</nav>
<router-outlet></router-outlet>
`
})
export class App {
get loggedIn() { return loggedIn; }
toggle() { loggedIn = !loggedIn; }
}
const routes = [
{ path: '', component: Home },
{ path: 'protected', component: Protected, canActivate: [authGuard] }
];
bootstrapApplication(App, {
providers: [provideRouter(routes, withHashLocation())]
});
<app-root></app-root>
Example explained
authGuard
: Returnstrue
when logged in; otherwise returns aUrlTree
that redirects to/
.inject(Router)
: Accesses the Router to create a redirectUrlTree
inside the guard function.canActivate
: Applies the guard to the/protected
route.- Toggle: The button flips the loggedIn state to test both branches.
Guard return types: Return boolean
, UrlTree
, or an observable/promise of those.