# Routing

Course: [Angular](https://www.devtutorials.ai/learn/angular) · Section: Navigation
Source: https://www.devtutorials.ai/learn/angular/navigation/routing

> Mapping URLs to components with the Angular Router: route configuration, router-outlet, routerLink, programmatic navigation, redirects, route and query parameters, child, wildcard and lazy-loaded routes.

## What Is Routing?

**Routing** connects each URL to one screen. For example, `/products` opens the product list and `/cart` opens the cart.

An Angular app is a **single-page application**: the browser downloads one page, and Angular changes the content inside it. Routing gives each screen its own URL without loading a new page.

### Real-World Example

In a project management app, `/projects/42/tasks` opens the task board of project 42. A colleague who opens this link sees the same board.

The Back button returns to the project list, and the header and menu never reload.

<figure className="ngc-fig">
<svg viewBox="0 0 640 140" role="img" aria-label="The router reads the URL, finds the matching route and shows its component in the router outlet.">
<defs><marker id="ngc-r1" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><g className="ngc-step ngc-t1"><g className="ngc-node ngc-soft c-blue"><rect x="6" y="36" width="142" height="60" rx="9" /><text x="77.0" y="63" textAnchor="middle" style={{fontSize:"14px"}}>/products/7</text><text className="ngc-sub" x="77.0" y="81" textAnchor="middle">URL</text></g></g><line className="ngc-link ngc-flow c-slate" x1="150" y1="66" x2="162" y2="66" markerEnd="url(#ngc-r1)" /><g className="ngc-step ngc-t2"><g className="ngc-node ngc-solid c-orange"><rect x="164" y="36" width="142" height="60" rx="9" /><text x="235.0" y="63" textAnchor="middle" style={{fontSize:"14px"}}>Router</text><text className="ngc-sub" x="235.0" y="81" textAnchor="middle">reads the URL</text></g></g><line className="ngc-link ngc-flow c-slate" x1="308" y1="66" x2="320" y2="66" markerEnd="url(#ngc-r1)" /><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-purple"><rect x="322" y="36" width="142" height="60" rx="9" /><text x="393.0" y="63" textAnchor="middle" style={{fontSize:"14px"}}>products/:id</text><text className="ngc-sub" x="393.0" y="81" textAnchor="middle">matching route</text></g></g><line className="ngc-link ngc-flow c-slate" x1="466" y1="66" x2="478" y2="66" markerEnd="url(#ngc-r1)" /><g className="ngc-step ngc-t4"><g className="ngc-node ngc-soft c-green"><rect x="480" y="36" width="142" height="60" rx="9" /><text x="551.0" y="63" textAnchor="middle" style={{fontSize:"14px"}}>ProductDetail</text><text className="ngc-sub" x="551.0" y="81" textAnchor="middle">shown in the outlet</text></g></g><text className="ngc-note" x="320" y="124" textAnchor="middle">Only the outlet changes. The header and menu stay on the screen.</text>
</svg>
<figcaption>From URL to component.</figcaption>
</figure>

## Why Routing?

Without routing, the URL never changes. So the user cannot save or share a screen, and the browser's Back button closes the app.

Benefits of routing:

- **Shareable URLs:** each screen has its own URL. Users can save it as a bookmark or send it to another person.
- **Back and Forward buttons work** the same way as on other websites.
- **Fast screen changes:** Angular replaces only the part of the page that changes. The browser does not download a new page.
- **Clear structure:** each screen is one component, and one list connects each URL to its component.
- **Faster start:** the browser can download a screen only when the user opens it. This is called lazy loading, and you learn it at the end of this lesson.

| Point | Without routing | With routing |
|---|---|---|
| How the screen changes | `@if` blocks and a variable | The URL chooses the screen |
| User refreshes the page | The app opens the first screen again | The same screen opens again |
| Save or share a screen | Not possible | Copy the URL |
| Browser's Back button | Leaves the app | Opens the previous screen |

## Angular Router

The **Angular Router** is the part of Angular that does routing. It is in the package `@angular/router`.

The router does three things:

1. It reads the URL in the browser.
2. It finds the route that matches this URL.
3. It displays the component of that route.

You use these parts of the router in this lesson:

- **`Routes`:** the list of routes. Each route connects a URL path to a component.
- **`provideRouter(routes)`:** turns on the router and gives it your list of routes.
- **`<router-outlet />`:** the place in the template where the router displays the component.
- **`routerLink`:** makes a link open a route without reloading the page.
- **`Router`:** a service that opens a route from your TypeScript code.
- **`withComponentInputBinding()`:** gives the values from the URL to the component's `@Input()` properties.

## Setting Up Routing

You do not need to install anything. When you create a project with `ng new`, Angular prepares routing for you. To use it, you work with three things.

### Step 1: Write Your Routes in `app.routes.ts`

`app.routes.ts` holds the list of your routes. In a new project, the list is empty (`[]`):

```ts
// app.routes.ts
import { Routes } from '@angular/router';

export const routes: Routes = [];
```

You add one object to this list for each screen of your app. The next section shows how.

### Step 2: The Router Gets Your List in `app.config.ts`

`app.config.ts` contains the settings that Angular reads when the app starts. Here, `provideRouter(routes)` turns on the router and gives it your list of routes:

```ts
// 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)],
};
```

The CLI already wrote this file. You do not need to change it.

### Step 3: Add `<router-outlet />` to the `App` Template

The router needs a place on the screen where it can display the components. You mark this place with `<router-outlet />` in the template of the `App` component. You learn it in the section "Router Outlet" below.

In the practice blocks below, the list of routes is written in `app.ts`, not in `app.routes.ts`. This way you can read all the code in one file. The router still gets the list in the same way.

## Route Configuration

Each route is an object. Most routes have two properties: `path` and `component`.

```ts
export const routes: Routes = [
  { path: '', component: Home },              // localhost:4200/
  { path: 'products', component: Products },  // localhost:4200/products
  { path: 'cart', component: Cart, title: 'Your cart' },
];
```

What each property does:

- **`path`** is the part of the URL after the domain. Write it without `/` at the start. The empty path `''` is the home page.
- **`component`** is the component that the router displays when the URL matches the path.
- **`title`** is the text in the browser tab for this page.

### The Order of Routes

The router checks the routes from top to bottom and uses the **first** route that matches. If two routes match the same URL, the lower route is never used.

## Router Outlet (`router-outlet`)

`<router-outlet />` marks the place in the template where the router displays the component of the current route.

The content outside the outlet, such as a header or a footer, stays on the screen. Only the content in the outlet changes when the URL changes.

```angular runnable title="Routes and an outlet"
import { Component } from '@angular/core';
import { Routes, RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-home',
  template: `<p>Welcome to Gadget Hub. Browse our latest products.</p>`,
})
export class Home {}

export const routes: Routes = [
  { path: '', component: Home },
];

@Component({
  selector: 'app-root',
  imports: [RouterOutlet],
  template: `
    <header><b>Gadget Hub</b></header>
    <router-outlet />
    <footer>© Gadget Hub</footer>
  `,
})
export class App {}
```

The URL is empty, so the router uses the route with `path: ''`. It displays the `Home` component between the header and the footer.

## `RouterLink`

A normal link with `href` makes the browser download the whole app again. A link with `routerLink` tells the router to change the URL and change the component in the outlet. The page does not reload.

How to write it:

- For a fixed path, write `routerLink="/products"`.
- For a path with a value, use an array: `[routerLink]="['/products', product.id]"`. This creates the path `/products/7` when `product.id` is 7.
- Add `routerLinkActive="active"` to add the CSS class `active` to the link while its route is open. Use it to highlight the current menu item.

```angular runnable title="Navigating with routerLink"
import { Component } from '@angular/core';
import { Routes, RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';

@Component({ selector: 'app-home', template: `<p>Home: today's deals.</p>` })
export class Home {}

@Component({ selector: 'app-products', template: `<p>Products: laptops, phones and tablets.</p>` })
export class Products {}

@Component({ selector: 'app-cart', template: `<p>Cart: 2 items.</p>` })
export class Cart {}

export const routes: Routes = [
  { path: '', component: Home },
  { path: 'products', component: Products },
  { path: 'cart', component: Cart },
];

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, RouterLink, RouterLinkActive],
  template: `
    <nav>
      <a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">Home</a> |
      <a routerLink="/products" routerLinkActive="active">Products</a> |
      <a routerLink="/cart" routerLinkActive="active">Cart</a>
    </nav>
    <router-outlet />
  `,
  styles: `.active { font-weight: bold; color: #c3002f; }`,
})
export class App {}
```

Click the links. The text below the menu changes to the selected page, and the link of the open page becomes bold and red.

For the home link, add `[routerLinkActiveOptions]="{ exact: true }"`. Without it, the home link stays highlighted on every page, because every URL starts with `/`.

## Programmatic Navigation

**Programmatic navigation** means opening a route from TypeScript code, not from a link. For example, after the user places an order, the code opens the "Order confirmed" page.

To do this, inject the `Router` service and call one of these methods:

| Method | Example | When to use it |
|---|---|---|
| `navigate()` | `router.navigate(['/orders', id])` | You build the path from separate parts |
| `navigateByUrl()` | `router.navigateByUrl('/orders/7')` | You already have the full path as text |

```angular runnable title="Navigating after an action"
import { Component, inject } from '@angular/core';
import { Router, Routes, RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-checkout',
  template: `
    <p>Checkout: 2 items, total $55</p>
    <button (click)="placeOrder()">Place order</button>
  `,
})
export class Checkout {
  private router = inject(Router);

  placeOrder() {
    const orderId = 1042; // in a real app, returned by the server
    this.router.navigate(['/order-confirmed', orderId]);
  }
}

@Component({ selector: 'app-confirmed', template: `<p>Thank you! Your order is confirmed.</p>` })
export class Confirmed {}

export const routes: Routes = [
  { path: '', component: Checkout },
  { path: 'order-confirmed/:id', component: Confirmed },
];

@Component({
  selector: 'app-root',
  imports: [RouterOutlet],
  template: `<router-outlet />`,
})
export class App {}
```

Click **Place order**. The `placeOrder()` method calls `navigate()`, and the router displays the confirmation page.

## Redirect Routes

A **redirect route** sends the user from one URL to another URL. It is often used to open a start page when the URL is empty.

```ts
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
{ path: 'home', redirectTo: 'dashboard' },
```

- **`redirectTo`** is the path to open instead.
- **`pathMatch: 'full'`** means: redirect only when the **whole** URL is empty. Without it, the empty path matches the start of every URL, and the redirect runs on every page.
- Redirects also help when you rename a page. Users with an old bookmark still reach the new page.

## Route Parameters

A **route parameter** is a part of the path that can change. You write it with a colon (`:`) before its name, for example `products/:id`.

The URLs `/products/7` and `/products/12` both match `products/:id`. For the first URL, `id` is `'7'`. For the second URL, `id` is `'12'`. One route and one component can display any product.

### Read the Parameter with `@Input()`

You know `@Input()` from the lesson Component Communication. A parent component uses it to give a value to a child component.

The router can give values in the same way. It puts the value of a route parameter into an `@Input()` property that has the **same name**. For the route `products/:id`, write this in the component:

```ts
@Input() id = '';
```

To turn on this feature, add `withComponentInputBinding()` to `provideRouter()` in `app.config.ts`:

```ts
providers: [provideRouter(routes, withComponentInputBinding())],
```

The practice blocks already have this setting.

### When the Parameter Changes

The user can go from `/products/7` to `/products/12`. Both URLs use the same route, so Angular keeps the same component on the screen. The router only changes the value of `id` from `'7'` to `'12'`, and the template displays the new product.

```angular runnable title="Reading a route parameter"
import { Component, Input } from '@angular/core';
import { Routes, RouterOutlet, RouterLink } from '@angular/router';

const PRODUCTS: Record<string, string> = { '7': 'Wireless Mouse', '12': 'Mechanical Keyboard' };

@Component({
  selector: 'app-product-detail',
  template: `<p>Product #{{ id }}: {{ name() }}</p>`,
})
export class ProductDetail {
  @Input() id = '';

  name() {
    return PRODUCTS[this.id] ?? 'Not found';
  }
}

export const routes: Routes = [
  { path: '', redirectTo: 'products/7', pathMatch: 'full' },
  { path: 'products/:id', component: ProductDetail },
];

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, RouterLink],
  template: `
    <a routerLink="/products/7">Mouse</a> | <a routerLink="/products/12">Keyboard</a>
    <router-outlet />
  `,
})
export class App {}
```

The empty URL redirects to `products/7`. Click **Keyboard**. The component stays on the screen and displays product 12.

## Query Parameters

**Query parameters** are extra values at the end of the URL, after a `?` sign. For example: `/search?q=laptop&page=2`. Each value has a name (`q`, `page`) and a value (`laptop`, `2`).

Use query parameters for filters, sorting and page numbers. The screen stays the same, but it shows different data.

| Point | Route parameter | Query parameter |
|---|---|---|
| Example URL | `/products/7` | `/products?sort=price` |
| Written in the route's path | Yes: `products/:id` | No |
| Required | Yes. Without it, the route does not match | No |
| Common use | Which item to display | Filter, sort order, page number |

In a link, add query parameters with `[queryParams]`. In the component, read them with an `@Input()` property that has the same name, the same as route parameters.

A query parameter can be missing from the URL. Then the property is `undefined`, so write a `?` after its name: `@Input() category?: string;`.

```angular runnable title="Filtering with query parameters"
import { Component, Input } from '@angular/core';
import { Routes, RouterOutlet, RouterLink } from '@angular/router';

const ALL = [
  { name: 'Laptop', category: 'computers' },
  { name: 'Monitor', category: 'computers' },
  { name: 'Headphones', category: 'audio' },
];

@Component({
  selector: 'app-products',
  template: `
    <p>Category: {{ category ?? 'all' }}</p>
    @for (p of list(); track p.name) {
      <p>• {{ p.name }}</p>
    }
  `,
})
export class Products {
  @Input() category?: string;

  list() {
    return this.category ? ALL.filter(p => p.category === this.category) : ALL;
  }
}

export const routes: Routes = [{ path: '', component: Products }];

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, RouterLink],
  template: `
    <a routerLink="/">All</a> |
    <a routerLink="/" [queryParams]="{ category: 'computers' }">Computers</a> |
    <a routerLink="/" [queryParams]="{ category: 'audio' }">Audio</a>
    <router-outlet />
  `,
})
export class App {}
```

Click **Computers** or **Audio**. The URL gets `?category=...`, and the list shows only the products of that category.

From TypeScript code, pass query parameters as an option: `router.navigate(['/products'], { queryParams: { category: 'audio' } })`.

## Child Routes

**Child routes** are routes inside another route. They display a component inside another component.

The parent component has its own `<router-outlet />`. The routes in its `children` list are displayed in this outlet. A settings page with tabs is a common example: the tabs stay on the screen, and the content below them changes.

<figure className="ngc-fig">
<svg viewBox="0 0 640 232" role="img" aria-label="Nested layout: App outlet contains Settings, whose own outlet shows Profile or Security child routes.">
<defs><marker id="ngc-r2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><g className="ngc-step ngc-t1"><g className="ngc-node ngc-soft c-blue"><rect x="10" y="8" width="620" height="186" rx="12" /><text x="30" y="32" style={{fontSize:"14px"}}>App: header + &lt;router-outlet&gt;</text></g></g><g className="ngc-step ngc-t2"><g className="ngc-node ngc-soft c-purple"><rect x="40" y="46" width="560" height="134" rx="10" /><text x="60" y="70" style={{fontSize:"14px"}}>Settings (path: settings): tabs + &lt;router-outlet&gt;</text></g></g><g className="ngc-step ngc-t3"><g className="ngc-node ngc-soft c-green"><rect x="70" y="92" width="240" height="70" rx="9" /><text x="190.0" y="124" textAnchor="middle" style={{fontSize:"15px"}}>Profile</text><text className="ngc-sub" x="190.0" y="142" textAnchor="middle">settings/profile</text></g></g><g className="ngc-step ngc-t4"><g className="ngc-node ngc-soft c-orange"><rect x="330" y="92" width="240" height="70" rx="9" /><text x="450.0" y="124" textAnchor="middle" style={{fontSize:"15px"}}>Security</text><text className="ngc-sub" x="450.0" y="142" textAnchor="middle">settings/security</text></g></g><text className="ngc-note" x="320" y="220" textAnchor="middle">A child route renders inside its parent's outlet, so the parent's tabs stay visible.</text>
</svg>
<figcaption>Child routes render inside the parent component.</figcaption>
</figure>

```angular runnable title="A settings page with child routes"
import { Component } from '@angular/core';
import { Routes, RouterOutlet, RouterLink } from '@angular/router';

@Component({ selector: 'app-profile', template: `<p>Profile: name, email and photo.</p>` })
export class Profile {}

@Component({ selector: 'app-security', template: `<p>Security: password and two-factor login.</p>` })
export class Security {}

@Component({
  selector: 'app-settings',
  imports: [RouterOutlet, RouterLink],
  template: `
    <h4>Settings</h4>
    <a routerLink="profile">Profile</a> | <a routerLink="security">Security</a>
    <router-outlet />
  `,
})
export class Settings {}

export const routes: Routes = [
  { path: '', redirectTo: 'settings/profile', pathMatch: 'full' },
  {
    path: 'settings',
    component: Settings,
    children: [
      { path: 'profile', component: Profile },
      { path: 'security', component: Security },
    ],
  },
];

@Component({
  selector: 'app-root',
  imports: [RouterOutlet],
  template: `<b>My Account</b><router-outlet />`,
})
export class App {}
```

The links in `Settings` start without `/` (`profile`, not `/profile`). So the router adds them to the current path, and they open `settings/profile` and `settings/security`.

## Wildcard Routes

A **wildcard route** has the path `'**'`. It matches **every** URL. Use it to display a "Page not found" screen when no other route matches the URL.

```ts
export const routes: Routes = [
  { path: '', component: Home },
  { path: 'products', component: Products },
  { path: '**', component: NotFound }, // always last
];
```

In the next example, click **Old promo link**. No route has the path `old-promo`, so the wildcard route displays `NotFound`.

```angular runnable title="A page-not-found route"
import { Component } from '@angular/core';
import { Routes, RouterOutlet, RouterLink } from '@angular/router';

@Component({ selector: 'app-home', template: `<p>Home page</p>` })
export class Home {}

@Component({
  selector: 'app-not-found',
  imports: [RouterLink],
  template: `<p>404: this page does not exist. <a routerLink="/">Go home</a></p>`,
})
export class NotFound {}

export const routes: Routes = [
  { path: '', component: Home },
  { path: '**', component: NotFound },
];

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, RouterLink],
  template: `
    <a routerLink="/">Home</a> | <a routerLink="/old-promo">Old promo link</a>
    <router-outlet />
  `,
})
export class App {}
```

Always put the `'**'` route last. The router uses the first route that matches. If `'**'` is first, it matches every URL, and the router never uses the routes below it.

## Lazy-Loaded Routes

Normally, the browser downloads the code of every component when the app starts. A big app then starts slowly.

With **lazy loading**, the code of a page is in a separate file. The browser downloads this file only when the user opens the page for the first time. So the app starts faster.

### `loadComponent`: Lazy-Load One Page

Use `loadComponent` instead of `component`. Its function uses `import()` to download the file:

```ts
export const routes: Routes = [
  { path: '', component: Home }, // downloaded when the app starts
  {
    path: 'reports',
    loadComponent: () => import('./reports/reports').then(m => m.Reports),
  },
];
```

### `loadChildren`: Lazy-Load a Group of Pages

To lazy-load a part of the app that has its own child routes, use `loadChildren`:

```ts
{
  path: 'admin',
  loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES),
}
```

### When to Use Lazy Loading

- **Use it for** big pages and pages that few users open, such as reports, admin pages and settings.
- **Do not use it for** the home page and the pages that most users open first.
- **Limitation:** the first time the user opens a lazy page, the user waits while its file downloads.

The practice blocks have only one file, `app.ts`, so this section shows lazy loading as code only. In a CLI project, `ng build` puts each lazy page in its own file.

## Common Mistakes

<div className="ngc-mistakes">

| Mistake | What you see | Fix |
|---|---|---|
| No `<router-outlet />` in the template | The URL changes, but no page is displayed | Add `<router-outlet />` to the parent template |
| Using `href` instead of `routerLink` | The whole app reloads on every click | Use `routerLink` |
| `/` at the start of a path: `path: '/products'` | Error: the path cannot start with a slash | Write `path: 'products'` |
| The `'**'` route is first | Every URL displays "Page not found" | Put the `'**'` route last |
| Empty-path redirect without `pathMatch: 'full'` | The redirect runs on every page, or the wrong page opens | Add `pathMatch: 'full'` |
| No `withComponentInputBinding()` in `provideRouter()` | The `@Input()` property for the parameter stays empty | Add `withComponentInputBinding()` |
| `@Input()` name is different from the parameter name | The property stays empty | Use the same name: `:id` needs `@Input() id` |

</div>

## Check Yourself

## Exercises

## What to Remember

- A route connects a URL path to a component. `provideRouter(routes)` turns on the router, `<router-outlet />` displays the component, and `routerLink` or `Router.navigate()` opens a route.
- `:id` in a path is a route parameter, and `?name=value` adds query parameters. Read both with `@Input()` properties that have the same names.
- Use `redirectTo` with `pathMatch: 'full'` for the empty URL, and `children` for pages inside pages. Put the `'**'` route last for "Page not found", and use `loadComponent` to download a page only when the user opens it.

Next: **RxJS Fundamentals**, how Observables send values over time and how operators change those values.
