Chapter 7: Routing Basics
Routing Basics — this is where your Angular app stops being a single page and becomes a real multi-page application with navigation, deep linking, and clean URLs.
In modern Angular (v19–21 as of 2026), routing is standalone-first, meaning no NgModule needed for most projects. Everything uses functional APIs (provideRouter, withComponentInputBinding, etc.), and it’s much cleaner and more type-safe than the old RouterModule.forRoot() style.
We’ll go slow and detailed, like always — with complete, runnable examples you can copy-paste into your existing project.
1. What is Angular Routing? (Quick overview)
Routing lets you:
- Define different views/pages (components) for different URLs
- Navigate between them without full page reloads (Single Page App magic)
- Pass data via route parameters (e.g. /user/123)
- Show content in a placeholder (<router-outlet>)
- Support lazy loading, guards, resolvers (later chapters)
2. Setting up the Router (Standalone APIs – 2026 style)
When you created your project with:
|
0 1 2 3 4 5 6 |
ng new my-app --standalone --routing |
Angular already set up routing for you! Look at src/main.ts (or src/app/app.config.ts if using provideIn style):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// src/main.ts (or app.config.ts) import { bootstrapApplication } from '@angular/platform-browser'; import { provideRouter } from '@angular/router'; import { AppComponent } from './app/app.component'; import { routes } from './app/app.routes'; // ← this file! bootstrapApplication(AppComponent, { providers: [ provideRouter(routes) // ← registers the router globally ] }); |
And src/app/app.routes.ts (this is where all routes live):
|
0 1 2 3 4 5 6 7 8 9 10 11 |
// src/app/app.routes.ts import { Routes } from '@angular/router'; export const routes: Routes = [ // We'll fill this soon ]; |
That’s it — no more RouterModule.forRoot()!
3. Creating Our First Routes
Let’s build a simple app with 3 pages:
- Home (/)
- About (/about)
- User Detail (/user/:id) – with route parameter
First, generate some components:
|
0 1 2 3 4 5 6 7 8 |
ng generate component home --standalone ng generate component about --standalone ng generate component user-detail --standalone |
Now update app.routes.ts:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// src/app/app.routes.ts import { Routes } from '@angular/router'; import { HomeComponent } from './home/home.component'; import { AboutComponent } from './about/about.component'; import { UserDetailComponent } from './user-detail/user-detail.component'; export const routes: Routes = [ { path: '', component: HomeComponent }, // default / home { path: 'about', component: AboutComponent }, { path: 'user/:id', component: UserDetailComponent }, // :id = route param { path: '**', redirectTo: '' } // wildcard → redirect to home ]; |
4. Displaying Routes – The RouterOutlet
In app.component.html (your root component), replace everything with:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!-- src/app/app.component.html --> <nav> <a routerLink="/">Home</a> | <a routerLink="/about">About</a> | <a routerLink="/user/123">User 123</a> | <a routerLink="/user/456">User 456</a> </nav> <main> <router-outlet></router-outlet> <!-- ← magic placeholder! --> </main> |
- <router-outlet> is where Angular renders the component matching the current URL
- routerLink is the Angular way to navigate (instead of <a href> – prevents full reload)
Add some basic content to each component:
home.component.html
|
0 1 2 3 4 5 6 7 |
<h1>Welcome to My Angular App</h1> <p>This is the home page.</p> |
about.component.html
|
0 1 2 3 4 5 6 7 |
<h1>About Us</h1> <p>We are learning modern Angular in 2026!</p> |
user-detail.component.html
|
0 1 2 3 4 5 6 7 |
<h1>User Detail</h1> <p>Showing details for user ID: <strong>{{ userId() }}</strong></p> |
5. Route Parameters & ActivatedRoute (Getting :id)
In user-detail.component.ts — we need to read the :id from the URL.
Modern way with inject() + ActivatedRoute:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// src/app/user-detail/user-detail.component.ts import { Component, computed, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { toSignal } from '@angular/core/rxjs-interop'; @Component({ selector: 'app-user-detail', standalone: true, templateUrl: './user-detail.component.html', styleUrl: './user-detail.component.css' }) export class UserDetailComponent { private route = inject(ActivatedRoute); // Convert params observable → signal (modern & reactive!) params = toSignal(this.route.params, { initialValue: {} }); // Computed signal for clean access userId = computed(() => this.params()?.['id'] ?? 'Unknown'); } |
Now when you click <a routerLink=”/user/123″>:
- URL becomes /user/123
- userId() shows 123
- Change to /user/456 → auto-updates to 456 (thanks to signals!)
Alternative (older but still common) – using snapshot:
|
0 1 2 3 4 5 6 |
userId = this.route.snapshot.paramMap.get('id') ?? 'Unknown'; |
But toSignal() + computed() is reactive — perfect if params change without full navigation (e.g., same component reused).
6. Bonus: Active Link Styling & routerLinkActive
Make navigation highlight the current page:
Update app.component.html nav:
|
0 1 2 3 4 5 6 7 8 9 10 11 |
<nav> <a routerLink="/" routerLinkActive="active">Home</a> | <a routerLink="/about" routerLinkActive="active">About</a> | <a routerLink="/user/123" routerLinkActive="active">User 123</a> | <a routerLink="/user/456" routerLinkActive="active">User 456</a> </nav> |
Add CSS in app.component.css:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
nav a { text-decoration: none; color: #333; padding: 8px 12px; margin: 0 4px; } nav a.active { background-color: #007bff; color: white; border-radius: 4px; } |
→ Current page gets blue background — nice UX!
7. Quick Comparison: Old vs Modern Routing (2026)
| Feature | Old (NgModule style) | Modern (Standalone + functional) |
|---|---|---|
| Setup | RouterModule.forRoot(routes) | provideRouter(routes) |
| Lazy loading | loadChildren: () => import(…).then(m => m.Module) | loadComponent: () => import(‘./lazy.component’) |
| Input binding | Manual | withComponentInputBinding() (later) |
| Params access | route.snapshot or params.subscribe | toSignal(route.params) + computed |
| Imports needed | RouterModule in module | Nothing – built-in |
8. Mini Practice Task (do this now!)
- Add a new route: /contact with a ContactComponent
- Add a link in nav: <a routerLink=”/contact” routerLinkActive=”active”>Contact</a>
- In UserDetailComponent, show a fake user object:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
users = { '123': { name: 'Amit Sharma', city: 'Airoli' }, '456': { name: 'Priya Patel', city: 'Mumbai' } }; user = computed(() => { const id = this.userId(); return this.users[id as keyof typeof this.users] ?? { name: 'Not Found', city: '?' }; }); |
Template:
|
0 1 2 3 4 5 6 7 |
<p>Name: {{ user().name }}</p> <p>City: {{ user().city }}</p> |
- Navigate to /user/123 and /user/999 → see how it handles missing users
Summary of what you learned today
- Set up router with provideRouter(routes)
- Define routes with { path, component }
- Use <router-outlet> to render routed components
- Navigate with routerLink
- Read route params with ActivatedRoute + toSignal()
- Style active links with routerLinkActive
You’ve now got full navigation working — your app feels like a real website!
Next chapter is usually Advanced Routing (lazy loading, child routes, route guards, resolvers) or HTTP & Async Operations (fetching data). Which one would you like to tackle next? Or want to debug/add more features to this routing setup? Just say the word — I’m right here! 😊
