Angular Routing Explained — Complete Guide with Guards & Lazy Loading
Angular routing lets you build single-page applications with multiple views, all without full page reloads. This complete guide explains route definitions, navigation, guards, lazy loading, and more.
What You’ll Learn
- Configure the Angular Router with route definitions
- Navigate between views using
routerLinkand programmatic methods - Read route parameters and query parameters
- Protect routes with guards (auth, unsaved changes)
- Lazy-load feature modules for better performance
- Handle 404 pages and redirects
Why Routing Matters
Single-page applications feel like native apps because they swap content without refreshing the page. Every DodaTech product — Doda Browser, DodaZIP, and Durga Antivirus Pro — uses routing to deliver seamless experiences. When you navigate from the Durga dashboard to the scan results page, routing ensures the transition is instant and the URL is bookmarkable. Angular Router makes this trivial to implement.
flowchart LR
A["Angular Basics & Forms"] --> B["**Angular Routing**"]
B --> C["Angular HTTP & Services"]
style B fill:#f97316,stroke:#c2410c,color:#fff
How the Angular Router Works
Think of the Router as a map for your app. When a user clicks a link or types a URL, the Router:
- Looks at the browser URL
- Matches it against your route configuration
- Renders the corresponding component in a
<router-outlet> - Updates the browser URL without reloading the page
flowchart TD
A["Browser URL"] --> B["Router"]
B --> C["Route Config"]
C --> D["Router Outlet"]
D --> E["Matched Component"]
F["RouterLink"] -->|Click| G["Navigate"]
G --> B
D --> H["Nested Routes"]
H --> I["Child Router Outlet"]
Setting Up the Router
If you created your project with --routing, you already have app.routes.ts. If not, here’s how to set it up:
// app.routes.ts
import { Routes } from "@angular/router";
import { HomeComponent } from "./home/home.component";
import { AboutComponent } from "./about/about.component";
// Route config: an array of route objects
export const routes: Routes = [
{ path: "", component: HomeComponent, title: "Home" },
{ path: "about", component: AboutComponent, title: "About" }
];// app.config.ts
import { ApplicationConfig } from "@angular/core";
import { provideRouter } from "@angular/router";
import { routes } from "./app.routes";
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes)] // Give the router your config
};Each route object has:
path— The URL segment (empty string""means the root/home page)component— What to render when this path matchestitle— Sets the browser tab title automatically
Navigation — Two Ways to Move Between Pages
Template Method: routerLink
<nav>
<a routerLink="/">Home</a> <!-- Absolute path -->
<a routerLink="/about">About</a>
<a routerLink="/products">Products</a>
<a [routerLink]="['/products', 42]">Product 42</a> <!-- With param -->
</nav>
<!-- Where matched components render -->
<router-outlet />routerLink="/"— Navigates to the root. The/makes it absolute (from the root).[routerLink]="['/products', 42]"— Array syntax for routes with parameters. This produces/products/42.<router-outlet />— This is where the matched component gets rendered. It’s a placeholder.
Active Link Styling
<a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">Home</a>
<a routerLink="/about" routerLinkActive="active">About</a>routerLinkActive="active" adds the CSS class active when the current URL matches the link. For the home route, use { exact: true } so it only highlights when the URL is exactly /, not when it starts with /.
Programmatic Method: Router Service
import { Router } from "@angular/router";
constructor(private router: Router) {}
// Navigate to specific path
this.router.navigate(["/products", id]);
// Navigate relative to current route
this.router.navigate(["edit"], { relativeTo: this.route });
// Replace current history (no back button to this page)
this.router.navigate(["/login"], { replaceUrl: true });
// Navigate by URL string
this.router.navigateByUrl("/about");Why navigate programmatically? When you need to redirect after a form submission, login, or error — not just from a user clicking a link.
Route Parameters
Route parameters let you create dynamic URLs like /products/42 or /users/alice:
// app.routes.ts
const routes: Routes = [
{
path: "products/:id", // :id is a parameter placeholder
component: ProductDetailComponent,
title: "Product Detail"
}
];import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
@Component({ ... })
export class ProductDetailComponent implements OnInit {
productId!: number;
constructor(private route: ActivatedRoute, private router: Router) {}
ngOnInit() {
// Method 1: Snapshot — one-time read (not reactive)
this.productId = Number(this.route.snapshot.paramMap.get("id"));
// Method 2: Observable — reacts to param changes (preferred)
this.route.paramMap.subscribe(params => {
this.productId = Number(params.get("id"));
this.loadProduct(this.productId);
});
}
goNext() {
this.router.navigate(["/products", this.productId + 1]);
}
}Why use the Observable approach? If the user navigates from /products/1 to /products/2, Angular reuses the same component instance. The snapshot only has the initial value (1). The Observable emits the new value (2), so your component updates.
Query Parameters
Query parameters are the ?key=value part of a URL. Use them for search queries, pagination, and filters:
// Navigate to /search?q=angular&page=2
this.router.navigate(["/search"], {
queryParams: { q: "angular", page: 2 }
});
// Read query parameters
this.route.queryParamMap.subscribe(params => {
const query = params.get("q");
const page = Number(params.get("page")) || 1;
});<!-- In template -->
<a [routerLink]="['/search']" [queryParams]="{ q: 'angular', page: 2 }">Search</a>Nested Routes (Child Routes)
Real apps have layouts with sidebars, tabs, or nested navigation. Nested routes let one component contain its own <router-outlet>:
const routes: Routes = [
{
path: "dashboard",
component: DashboardLayoutComponent, // Has its own <router-outlet>
children: [
{ path: "", component: DashboardHomeComponent, title: "Dashboard" },
{ path: "analytics", component: AnalyticsComponent, title: "Analytics" },
{ path: "settings", component: SettingsComponent, title: "Settings" }
]
}
];<!-- DashboardLayoutComponent template -->
<div class="dashboard-layout">
<aside>
<a routerLink="/dashboard">Overview</a>
<a routerLink="/dashboard/analytics">Analytics</a>
<a routerLink="/dashboard/settings">Settings</a>
</aside>
<main>
<router-outlet /> <!-- Child components render here -->
</main>
</div>Now /dashboard/analytics renders AnalyticsComponent inside DashboardLayoutComponent’s <router-outlet>.
Navigation Guards
Guards are functions that protect routes — they decide whether a user can enter or leave a route:
// auth.guard.ts
import { inject } from "@angular/core";
import { CanActivateFn, Router } from "@angular/router";
export const authGuard: CanActivateFn = (route, state) => {
const router = inject(Router);
const isAuthenticated = localStorage.getItem("token");
if (!isAuthenticated) {
// Redirect to login with return URL
return router.parseUrl("/login?redirect=" + state.url);
}
return true; // Allow access
};// Apply to routes
const routes: Routes = [
{
path: "admin",
component: AdminComponent,
canActivate: [authGuard], // Guard runs before route activates
title: "Admin"
}
];The guard returns:
true— Allow navigationfalse— Block navigation (shows nothing)UrlTree— Redirect to a different URL (preferred)
Guard Types
| Guard | When It Runs | Use Case |
|---|---|---|
canActivate | Before entering a route | Auth check |
canActivateChild | Before entering any child route | Permission check |
canDeactivate | Before leaving a route | Unsaved changes warning |
canMatch | Before matching a route | Feature flags |
resolve | Before activating, prefetch data | Load data before showing page |
Lazy Loading — Faster Initial Loads
Lazy loading means splitting your app into chunks that load only when needed. Your initial bundle stays small, and users don’t download code they haven’t visited yet:
const routes: Routes = [
{
path: "admin",
loadComponent: () => import("./admin/admin.component").then(m => m.AdminComponent),
canActivate: [authGuard],
title: "Admin"
},
{
path: "products",
loadChildren: () => import("./products/products.routes").then(m => m.productRoutes)
}
];// products/products.routes.ts
import { Routes } from "@angular/router";
export const productRoutes: Routes = [
{ path: "", component: ProductListComponent, title: "Products" },
{ path: ":id", component: ProductDetailComponent, title: "Product Detail" }
];loadComponent loads a single component lazily. loadChildren loads an entire set of child routes. Both use dynamic import() syntax — a native JavaScript feature that returns a Promise.
Redirects & 404 Handling
const routes: Routes = [
{ path: "", component: HomeComponent },
{ path: "old-path", redirectTo: "/new-path", pathMatch: "full" },
{ path: "**", component: NotFoundComponent, title: "Page Not Found" }
];redirectTo— Redirects from one path to another automatically.pathMatch: "full"— Only redirect if the entire path matches (not just a prefix).path: "**"— The wildcard route catches any path that didn’t match anything above. Always put this last.
Scroll Behavior & Title Strategy
Angular Router can automatically manage scrolling and document titles:
// app.config.ts
import { provideRouter, withRouterConfig } from "@angular/router";
providers: [
provideRouter(routes,
withRouterConfig({
scrollPositionRestoration: "top", // Scroll to top on navigation
anchorScrolling: "enabled" // Enable #fragment scrolling
})
)
]Setting title on each route (title: "Home") automatically updates the browser tab title when navigating.
Common Mistakes
1. Not using the wildcard route for 404 pages
Without { path: '**', component: NotFoundComponent }, unknown paths show a blank page. Place the wildcard route last since Angular evaluates routes in order.
2. Using snapshot.paramMap when parameters can change
snapshot.paramMap.get("id") only reads the initial value. If the user navigates from /products/1 to /products/2, the component reuses and snapshot doesn’t update. Use paramMap.subscribe() instead.
3. Missing relativeTo for relative navigation
this.router.navigate(['edit']) navigates from the root (/edit), not from the current route. Add { relativeTo: this.route } to navigate relative to the active route.
4. Not implementing canDeactivate for forms
Users may accidentally leave a form with unsaved changes. Use canDeactivate guard to show a confirmation dialog.
5. Overusing eager loading for large feature modules
If every route uses eager loading, your initial bundle grows unnecessarily. Lazy load feature routes with loadComponent or loadChildren for better performance.
6. Incorrect routerLinkActive with nested routes
Without { exact: true }, the home link (/) stays highlighted on every page because all paths start with /. Always use exact matching for the root route.
Practice Questions
What is the difference between
snapshot.paramMapandparamMapObservable?snapshot.paramMapgives a one-time value that doesn’t update when parameters change.paramMapis an Observable that emits new values whenever parameters change.How do you protect a route from unauthorized users? Use
canActivateguard. The guard function checks for authentication (e.g., a token in localStorage) and returnstrue,false, or aUrlTreefor redirect.What does
loadComponentdo? It lazy-loads a component — the component’s code is split into a separate JavaScript chunk that downloads only when the user visits that route.How do you create nested routes? Add a
childrenarray to a parent route. The parent component needs its own<router-outlet>where child components render.What is the purpose of
pathMatch: 'full'? It ensures the redirect only happens when the URL matches the entire path, not just a prefix. Without it,/old-path/extrawould also match.
Challenge
Build a product catalog with: a home page, a lazy-loaded products list, a product detail page with route parameter :id, an auth guard on the checkout route, and a 404 page. Add active link styling to the navigation.
FAQ
Try It Yourself
Create a new Angular project with routing and implement:
- Three pages: Home, About, and Products (lazy-loaded)
- Navigation bar with
routerLinkandrouterLinkActive - Product detail page that reads an
:idparameter - An auth guard that redirects to
/loginif no token exists - A
<router-outlet>where pages render
// app.routes.ts — starter structure
import { Routes } from "@angular/router";
import { authGuard } from "./guards/auth.guard";
export const routes: Routes = [
{ path: "", loadComponent: () => import("./home/home.component").then(m => m.HomeComponent), title: "Home" },
{ path: "about", loadComponent: () => import("./about/about.component").then(m => m.AboutComponent), title: "About" },
{
path: "products",
loadChildren: () => import("./products/products.routes").then(m => m.productRoutes),
title: "Products"
},
{ path: "admin", loadComponent: () => import("./admin/admin.component").then(m => m.AdminComponent), canActivate: [authGuard] },
{ path: "**", loadComponent: () => import("./not-found/not-found.component").then(m => m.NotFoundComponent) }
];What’s Next
| Tutorial | Description |
|---|---|
| https://tutorials.dodatech.com/frameworks/angular/angular-http/ | Connect to backends with HTTP Client and services |
| https://tutorials.dodatech.com/frameworks/angular/angular-advanced/ | Signals, performance optimization, dynamic components |
| https://tutorials.dodatech.com/frameworks/angular/cli/ | Angular CLI commands for scaffolding and building |
Related topics: TypeScript, REST API, JavaScript/ES Modules.
What’s Next
Congratulations on completing this Angular Routing tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro