EthicsComputerCourses
Join training +91 90532 10052

Routing

Angular · Lesson 15 of 41 Intermediate 6 min read Updated

A single-page application still needs "pages": a home page, a menu, an about page, an account area. The Angular Router maps each URL to a component and swaps components inside the page as the user navigates, without reloading. The browser's back and forward buttons and bookmarks keep working.

In this lesson you will define routes, show them, link between them and navigate from code. The next lesson adds route parameters, guards and lazy loading.

Define your routes

A new project already has src/app/app.routes.ts and calls provideRouter(routes) in app.config.ts. A route is an object with at least a path and a component:

TypeScript
import { Routes } from '@angular/router';
import { Home } from './home/home';
import { MenuPage } from './menu-page/menu-page';
import { About } from './about/about';
import { NotFound } from './not-found/not-found';

export const routes: Routes = [
  { path: '', component: Home, title: 'Campus Canteen' },
  { path: 'menu', component: MenuPage, title: 'Menu | Campus Canteen' },
  { path: 'about', component: About, title: 'About | Campus Canteen' },
  { path: 'home', redirectTo: '', pathMatch: 'full' },
  { path: '**', component: NotFound, title: 'Not found' },
];
  • path is the part of the URL after the domain, without a leading slash. '' is the home page.
  • title sets the browser tab title when the route is active, which also helps accessibility.
  • redirectTo sends one URL to another. pathMatch: 'full' means "only when the whole URL matches", which you need for redirects from '' or short paths.
  • '**' is the wildcard route. It matches anything not matched earlier, so it is your 404 page.

The router checks routes in order and uses the first match. Always put the wildcard last; anything after it can never match.

Show routes with router-outlet

<router-outlet /> marks where the active route's component appears. Put it in the root component, around which you place things that stay on every page, such as a navigation bar:

TypeScript
import { Component } from '@angular/core';
import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, RouterLink, RouterLinkActive],
  template: `
    <nav>
      <a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">Home</a>
      <a routerLink="/menu" routerLinkActive="active">Menu</a>
      <a routerLink="/about" routerLinkActive="active">About</a>
    </nav>
    <main>
      <router-outlet />
    </main>
  `,
  styles: `
    .active {
      font-weight: bold;
      border-bottom: 2px solid #c3002f;
    }
  `,
})
export class App {}

Visiting http://localhost:4200/menu shows the navigation bar, then the MenuPage component inside <main>, and the tab title becomes "Menu | Campus Canteen". Visiting /xyz shows the not-found page.

Use routerLink instead of href for links inside your app:

  • href="/menu" makes the browser download the whole app again: a full page reload that loses all signals and state.
  • routerLink="/menu" lets the router change the URL and swap the component instantly.

For URLs built from data, bind an array of path segments: [routerLink]="['/orders', order.id]" produces /orders/104.

routerLinkActive="active" adds the active CSS class to the link while its route is showing, so users can see where they are. Because / is the start of every URL, the Home link needs [routerLinkActiveOptions]="{ exact: true }", or it would look active on every page.

Sometimes you navigate after an action, for example after an order is placed. Inject the Router and call navigate() with path segments, or navigateByUrl() with a full URL string:

TypeScript
import { Component, inject } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-about',
  template: `
    <h2>About us</h2>
    <button (click)="goToMenu()">See the menu</button>
  `,
})
export class About {
  private router = inject(Router);

  protected goToMenu() {
    this.router.navigate(['/menu']);
  }
}

Both methods return a promise that resolves to true when navigation succeeds. Use links for normal navigation (they work with right-click, "open in new tab" and screen readers), and code navigation only after actions.

Nested routes

A section of the app can have its own sub-pages. Give a route children and put a second <router-outlet /> in that route's component:

TypeScript
{
  path: 'account',
  component: Account,
  children: [
    { path: '', redirectTo: 'profile', pathMatch: 'full' },
    { path: 'profile', component: Profile },
    { path: 'orders', component: PastOrders },
  ],
},

The Account template contains its own small menu with relative links (routerLink="profile", no leading slash) and a <router-outlet />. Visiting /account redirects to /account/profile:

Output
My account
Profile | Past orders
Name: Priya Sharma

The outer outlet shows Account, and the inner outlet shows Profile.

Deploying: the server must know about routes

During development, ng serve handles every URL. On a real server, a user may open https://example.com/menu directly or press refresh. The server looks for a file called menu, finds nothing and returns a 404 error. The fix is to tell the server to send index.html for any URL that is not a real file, so Angular can read the URL and show the right page. For Apache (as in XAMPP), a .htaccess file next to index.html can do this:

Text
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.html [L]

Other servers (Nginx, Firebase Hosting, Netlify) have their own "fallback to index.html" setting.

Common mistakes

  • A leading slash in path. Write path: 'menu', not path: '/menu'; the router reports an error for the slash.
  • The wildcard route not last. Routes after '**' are never reached.
  • Using href for internal links. It reloads the whole app. Use routerLink.
  • Forgetting to import RouterLink. Without it, routerLink is just an unknown attribute and clicking does nothing useful. Import RouterLink, RouterLinkActive and RouterOutlet where you use them.

Key points

  • Routes in app.routes.ts map paths to components; provideRouter(routes) turns routing on.
  • <router-outlet /> shows the active route's component; nested routes use nested outlets.
  • Use routerLink for links, routerLinkActive to highlight the current page, and Router.navigate() from code.
  • redirectTo with pathMatch: 'full' handles redirects; '**' catches unknown URLs and must be last.
  • On a real server, configure a fallback to index.html so deep links and refreshes work.

Exercise

Add routing to a school website with three pages: Home (''), Admissions (admissions) and Contact (contact), each with its own tab title. Add a not-found page. In the root component, create a navigation bar with active-link highlighting. On the Contact page, add a button "Apply now" that navigates to Admissions from code.

Show solution
TypeScript
export const routes: Routes = [
  { path: '', component: SchoolHome, title: 'Sunrise Public School, Rohtak' },
  { path: 'admissions', component: Admissions, title: 'Admissions | Sunrise Public School' },
  { path: 'contact', component: Contact, title: 'Contact | Sunrise Public School' },
  { path: '**', component: NotFound, title: 'Page not found' },
];

@Component({
  selector: 'app-contact',
  template: `
    <h2>Contact us</h2>
    <p>Phone: 01262-000000</p>
    <button (click)="apply()">Apply now</button>
  `,
})
export class Contact {
  private router = inject(Router);

  protected apply() {
    this.router.navigate(['/admissions']);
  }
}

The root template contains three <a routerLink=... routerLinkActive="active"> links (with exact: true on Home) and a <router-outlet />. The wildcard route is last so that only unknown URLs reach the not-found page.

Quick Quiz

1. Where does the router display the component for the current URL?

2. Why use routerLink instead of href for links inside the app?

3. What does the path '**' match?

4. A user refreshes /menu on your Apache server and gets a 404. What is the fix?

Learn Angular with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call