Routing
Understand how Angular Router connects URLs to components and enables navigation without reloading the complete page.
Lesson content
What is Angular Routing? Angular Routing is the mechanism used to display different components based on the browser URL. It allows users to navigate between application pages without reloading the complete browser page. Routing Flow User clicks a link ↓ Angular Router reads the URL ↓ Finds the matching route ↓ Loads the component ↓ Displays it inside router-outlet Main Routing Parts Routes – Defines URL and component mappings provideRouter() – Registers Angular Router RouterLink – Navigates through template links RouterOutlet – Displays the active route component Router – Performs programmatic navigation ActivatedRoute – Provides route parameters and route data 1. Define Routes Routes are normally defined inside app.routes.ts . import { Routes } from '@angular/router'; import { HomeComponent } from './home.component'; import { ProductsComponent } from './products.component'; export const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'products', component: ProductsComponent } ]; Route Mapping URL ↓ Route Configuration ↓ Component / → HomeComponent /products → ProductsComponent 2. Register the Router Modern standalone Angular applications register routes using provideRouter() . import { ApplicationConfig } from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; export const appConfig: ApplicationConfig = { providers: [ provideRouter(routes) ] }; 3. Router Outlet The router-outlet marks the location where Angular displays the active routed component. import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; @Component({ selector: 'app-root', standalone: true, imports: [RouterOutlet], template: ` <h1>My Application</h1> <router-outlet /> ` }) export class AppComponent {} Outlet Flow URL: /products ↓ Products route matched ↓ ProductsComponent created ↓ Rendered inside router-outlet 4. RouterLink Use routerLink instead of normal page navigation for Angular application routes. import { Component } from '@angular/core'; import { RouterLink, RouterLinkActive } from '@angular/router'; @Component({ selector: 'app-navigation', standalone: true, imports: [ RouterLink, RouterLinkActive ], template: ` <a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }"> Home </a> <a routerLink="/products" routerLinkActive="active"> Products </a> ` }) export class NavigationComponent {} RouterLinkActive adds a CSS class when the associated route is active. 5. Programmatic Navigation Use the Router service when navigation must happen from TypeScript code. import { Component, inject } from '@angular/core'; import { Router } from '@angular/router'; @Component({ selector: 'app-login', standalone: true, template: ` <button (click)="login()"> Login </button> ` }) export class LoginComponent { private router = inject(Router); login() { const loginSuccessful = true; if (loginSuccessful) { this.router.navigate(['/dashboard']); } } } 6. Route Parameters Route parameters pass dynamic values through the URL. Route Configuration export const routes: Routes = [ { path: 'users/:id', loadComponent: () => import('./user-details.component') .then(module => module.UserDetailsComponent) } ]; Example URL /users/101 Reading Route Parameters import { Component, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'app-user-details', standalone: true, template: ` <p>User ID: {{ userId }}</p> ` }) export class UserDetailsComponent { private route = inject(ActivatedRoute); userId = this.route.snapshot.paramMap.get('id'); } Reactive Parameter Reading import { Component, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'app-user-details', standalone: true, template: ` <p>User ID: {{ userId }}</p> ` }) export class UserDetailsComponent { private route = inject(ActivatedRoute); userId = ''; constructor() { this.route.paramMap.subscribe(params => { this.userId = params.get('id') ?? ''; }); } } Use the reactive approach when the same component can remain active while its route parameter changes. 7. Query Parameters Query parameters provide optional URL information such as filters, sorting, and pagination. Example URL /products?page=2&category=mobile Navigate with Query Parameters this.router.navigate( ['/products'], { queryParams: { page: 2, category: 'mobile' } } ); Read Query Parameters this.route.queryParamMap.subscribe(params => { const page = params.get('page'); const category = params.get('category'); }); 8. Child Routes Child routes display nested pages inside a parent component. export const routes: Routes = [ { path: 'account', loadComponent: () => import('./account.component') .then(module => module.AccountComponent), children: [ { path: 'profile', loadComponent: () => import('./profile.component') .then(module => module.ProfileComponent) }, { path: 'orders', loadComponent: () => import('./orders.component') .then(module => module.OrdersComponent) } ] } ]; The parent component requires another router-outlet . <h2>My Account</h2> <nav> <a routerLink="profile">Profile</a> <a routerLink="orders">Orders</a> </nav> <router-outlet /> Child Route Flow /account/orders ↓ AccountComponent ↓ Parent router-outlet ↓ OrdersComponent 9. Lazy Loading Lazy loading downloads route code only when the user visits that route. Lazy-Load a Standalone Component export const routes: Routes = [ { path: 'dashboard', loadComponent: () => import('./dashboard.component') .then(module => module.DashboardComponent) } ]; Lazy-Load Child Routes export const routes: Routes = [ { path: 'admin', loadChildren: () => import('./admin/admin.routes') .then(module => module.ADMIN_ROUTES) } ]; Lazy loading reduces the initial JavaScript bundle and can improve application startup performance. 10. Redirect Routes export const routes: Routes = [ { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: 'home', loadComponent: () => import('./home.component') .then(module => module.HomeComponent) } ]; Use pathMatch: 'full' for an empty-path redirect so Angular matches the complete URL. 11. Wildcard Route A wildcard route handles URLs that do not match any defined route. export const routes: Routes = [ { path: '', loadComponent: () => import('./home.component') .then(module => module.HomeComponent) }, { path: '**', loadComponent: () => import('./not-found.component') .then(module => module.NotFoundComponent) } ]; Place the wildcard route last because Angular checks routes from top to bottom. 12. Route Guards Route guards control whether navigation is allowed. import { inject } from '@angular/core'; import { CanActivateFn, Router } from '@angular/router'; export const authGuard: CanActivateFn = () => { const authService = inject(AuthService); const router = inject(Router); return authService.isLoggedIn() ? true : router.createUrlTree(['/login']); }; Apply the Guard { path: 'dashboard', canActivate: [ authGuard ], loadComponent: () => import('./dashboard.component') .then(module => module.DashboardComponent) } Common Guard Types canActivate – Controls access to a route canActivateChild – Controls access to child routes canDeactivate – Controls leaving a route canMatch – Controls whether a route can match Frontend guards improve navigation behaviour, but secure authorization must always be enforced by the backend as well. 13. Route Resolvers A resolver loads required data before Angular activates a route. import { inject } from '@angular/core'; import { ResolveFn } from '@angular/router'; export const userResolver: ResolveFn<User> = route => { const userService = inject(UserService); return userService.getUser( route.paramMap.get('id')! ); }; Apply the Resolver { path: 'users/:id', resolve: { user: userResolver }, loadComponent: () => import('./user-details.component') .then(module => module.UserDetailsComponent) } Resolvers are useful for critical data, but navigation waits until the resolver completes. Important Routing Comparison Feature Purpose RouterLink Template-based navigation Router.navigate() Programmatic navigation ActivatedRoute Reads parameters, query parameters, and route data loadComponent Lazy-loads a standalone component loadChildren Lazy-loads child route configuration canActivate Controls route access resolve Loads data before route activation Best Practices Use standalone route configuration for modern Angular applications Lazy-load large features using loadComponent or loadChildren Place specific routes before wildcard routes Use RouterLink for internal application navigation Use route parameters for required identifiers Use query parameters for optional filters and pagination Return a redirect result from guards instead of calling navigation manually Do not depend only on client-side guards for security Avoid slow resolvers unless data is required before rendering Key Point Angular Routing maps browser URLs to components and updates the SPA view without a complete page reload. Easy Formula URL ↓ Route Configuration ↓ Guard ↓ Resolver ↓ Component ↓ router-outlet ↓ Page View