---
title: "How to Use Route and Query Parameters in Angular (with Signals & Inputs)"
date: "2024-02-25"
slug: "learn-route-parameters-in-angular-with-example"
author: "Dany Paredes"
canonical: "https://danywalls.com/learn-route-parameters-in-angular-with-example"
description: "Learn how to pass and read route parameters, query parameters, and matrix parameters in Angular using Signals, withComponentInputBinding, and ActivatedRoute."
---


When building modern web applications in Angular, passing data across routes and components is a daily requirement. While state management stores and shared injectable services are great for global state, the URL itself is often the cleanest, most shareable way to represent component state.

Whether you need to display a specific user profile by ID (`/user/123`), filter a product catalog with query parameters (`/products?category=tech&discount=true`), or scope parameters with matrix URLs, the Angular Router provides multiple robust mechanisms to pass and read parameters.

In this tutorial, we will explore:
1. **Required Route Parameters** (`:id`)
2. **Query Parameters** (`?key=value`)
3. **Matrix Parameters** (`;key=value`)
4. **Modern Signal Binding** using `withComponentInputBinding()` and `input()` signals

Let's begin by understanding the differences between these parameter types.

---

## 1. Required Route Parameters (`:id`) 🎯

Required route parameters are embedded directly into the URL path. You define dynamic segments in your route configuration using a colon (`:`):

```typescript
export const routes: Routes = [
  { 
    path: 'products/:id', 
    component: ProductDetailComponent 
  }
];
```

### Navigating to Route Parameters

You can navigate to dynamic route parameters either declaratively via `routerLink` in your template or imperatively in TypeScript via the `Router` service.

#### In Templates with `routerLink`:

```html
<a [routerLink]="['/products', product.id]">
  View {{ product.title }}
</a>
```

#### Imperatively in TypeScript:

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

@Component({
  standalone: true,
  selector: 'app-product-card',
  template: `<button (click)="openProduct(123)">View Details</button>`
})
export class ProductCardComponent {
  private readonly router = inject(Router);

  openProduct(id: number) {
    this.router.navigate(['/products', id]);
  }
}
```

The resulting URL will look like:
```text
https://danywalls.com/products/123
```

Now let's look at optional Query Parameters.

---

## 2. Query Parameters (`?key=value`) 🔍

Query parameters appear after the question mark (`?`) in the URL. They are ideal for optional filtering, pagination, search terms, or sorting options that do not alter the core route path.

### Passing Query Parameters

#### In Templates with `[queryParams]`:

```html
<a [routerLink]="['/search']" [queryParams]="{ term: 'angular', page: 2 }">
  Search Angular (Page 2)
</a>
```

#### Imperatively with `Router.navigate`:

```typescript
this.router.navigate(['/search'], {
  queryParams: { term: 'angular', page: 2 },
  queryParamsHandling: 'merge' // Preserves existing query params if needed
});
```

The resulting URL:
```text
https://danywalls.com/search?term=angular&page=2
```

Next, let's explore Matrix Parameters.

---

## 3. Matrix Parameters (`;key=value`) 🧱

Matrix parameters allow you to attach optional key-value pairs directly to a specific segment of the URL path separated by semicolons (`;`).

```html
<!-- In templates -->
<a [routerLink]="['/products', { color: 'blue', size: 'medium' }]">
  Filtered Products
</a>
```

Imperative navigation:

```typescript
this.router.navigate(['/products', { color: 'blue', size: 'medium' }]);
```

The resulting URL:
```text
https://danywalls.com/products;color=blue;size=medium
```

Now, let's look at the modern, elegant way to read all these parameters in Angular: Component Input Binding with Signals.

---

## 4. Modern Route Input Binding with Angular Signals ⚡

Historically, reading URL parameters required subscribing to `ActivatedRoute.paramMap` or `queryParamMap` Observables and manually managing subscriptions.

In modern Angular, you can bind route parameters **directly to component inputs** automatically using `withComponentInputBinding()`.

### Step 1: Enable `withComponentInputBinding` in `app.config.ts`

```typescript
import { ApplicationConfig } from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(
      routes,
      withComponentInputBinding() // 👈 Enables automatic input binding
    )
  ]
};
```

### Step 2: Read Route & Query Parameters using Signal `input()`

Once enabled, any route parameter, query parameter, or router data resolved for that route will be injected directly into matching `@Input` or signal `input()` properties in your component:

```typescript
import { Component, input, computed, inject } from '@angular/core';
import { ProductsService } from '../services/products.service';

@Component({
  standalone: true,
  selector: 'app-product-details',
  template: `
    @if (selectedProduct(); as product) {
      <article class="product-detail">
        <h2>{{ product.title }}</h2>
        <p>{{ product.description }}</p>
        <span class="price">\${{ product.price }}</span>

        @if (discount()) {
          <div class="badge">Special 10% Discount Applied!</div>
        }
      </article>
    }
  `
})
export class ProductDetailsComponent {
  private readonly productService = inject(ProductsService);

  // 1. Matches route path ':id' -> auto-injected as a Signal!
  readonly id = input.required<string>();

  // 2. Matches query param '?discount=true' -> auto-injected!
  readonly discount = input<string>();

  // 3. Reactive computed signal that automatically recalculates when 'id' changes
  readonly selectedProduct = computed(() => {
    const products = this.productService.products();
    return products.find(p => p.id === this.id());
  });
}
```

### Why Signal Input Binding is a Game Changer:
1. **Zero boilerplate**: No need to inject `ActivatedRoute` or manage RxJS subscriptions.
2. **Reactivity**: When the URL parameter changes (e.g. user navigates from `/products/1` to `/products/2`), the `id()` signal automatically updates, triggering computed signals and template re-renders cleanly.
3. **Type Safety**: Works seamlessly with `input.required<string>()` and transform functions.

---

## 5. Alternative: Reading Parameters with `ActivatedRoute` & `toSignal` 🔄

If you need access to the full `ActivatedRoute` state or want to create derived signals from `queryParamMap` in a service:

```typescript
import { Injectable, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { toSignal } from '@angular/core/rxjs-interop';
import { map } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class SearchStateService {
  private readonly route = inject(ActivatedRoute);

  // Convert queryParamMap observable into a reactive Signal
  readonly searchTerm = toSignal(
    this.route.queryParamMap.pipe(
      map(params => params.get('q') ?? '')
    ),
    { initialValue: '' }
  );
}
```

---

## Summary Comparison Matrix 📊

| Parameter Type | URL Syntax Example | Router Definition Required? | Best For |
| :--- | :--- | :---: | :--- |
| **Route Param** | `/products/123` | ✅ Yes (`:id`) | Essential entity identifiers, RESTful paths |
| **Query Param** | `/products?category=shoes` | ❌ No | Filters, search queries, pagination, flags |
| **Matrix Param** | `/products;size=large` | ❌ No | Scoped intermediate route parameters |

---

## Recap 🛠️

Managing URL state in Angular is cleaner and faster than ever:
- Use **`provideRouter(routes, withComponentInputBinding())`** to automatically pipe route data into component inputs.
- Use **`input()` and `computed()` signals** to reactively handle parameter updates with zero subscription management.
- Use **`[queryParams]`** for optional filters and state that should be shareable across bookmarks.

For more modern Angular architectural patterns, check out my articles on [How to Use ng-template, ng-container, and ng-content in Angular](/how-to-get-and-use-ng-template-ng-container-and-ng-content) and [Sharing Data Between Components in Angular](/how-to-share-data-between-components-in-angular)!

