How to Use Route and Query Parameters in Angular (with Signals & Inputs)

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:
- Required Route Parameters (
:id) - Query Parameters (
?key=value) - Matrix Parameters (
;key=value) - Modern Signal Binding using
withComponentInputBinding()andinput()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 (:):
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:
<a [routerLink]="['/products', product.id]">
View {{ product.title }}
</a>Imperatively in 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:
https://danywalls.com/products/123Now 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]:
<a [routerLink]="['/search']" [queryParams]="{ term: 'angular', page: 2 }">
Search Angular (Page 2)
</a>Imperatively with Router.navigate:
this.router.navigate(['/search'], {
queryParams: { term: 'angular', page: 2 },
queryParamsHandling: 'merge' // Preserves existing query params if needed
});The resulting URL:
https://danywalls.com/search?term=angular&page=2Next, 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 (;).
<!-- In templates -->
<a [routerLink]="['/products', { color: 'blue', size: 'medium' }]">
Filtered Products
</a>Imperative navigation:
this.router.navigate(['/products', { color: 'blue', size: 'medium' }]);The resulting URL:
https://danywalls.com/products;color=blue;size=mediumNow, 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
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:
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:
- Zero boilerplate: No need to inject
ActivatedRouteor manage RxJS subscriptions. - Reactivity: When the URL parameter changes (e.g. user navigates from
/products/1to/products/2), theid()signal automatically updates, triggering computed signals and template re-renders cleanly. - 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:
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()andcomputed()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 and Sharing Data Between Components in Angular!
These are my experiences learning and facing my daily challenges working with Angular.
Frequently Asked Questions
What is the difference between Route Parameters and Query Parameters in Angular?
Route parameters (e.g. /products/:id) are required dynamic segments defined directly in your route configuration path. Query parameters (e.g. /products?category=shoes&page=2) appear after the '?' symbol, are optional, and do not need to be registered in route definitions.
How do I bind route parameters directly to component inputs in Angular?
In your application config, enable 'withComponentInputBinding()' inside 'provideRouter(routes, withComponentInputBinding())'. Then in your component, declare an input signal with the matching name (e.g. id = input<string>()). Angular automatically binds the URL parameter to the signal input.
How do I read query parameters reactively using Angular Signals?
You can inject 'ActivatedRoute' and convert its 'queryParamMap' observable into a reactive Signal using the 'toSignal()' helper from '@angular/core/rxjs-interop', or automatically receive query params as inputs when 'withComponentInputBinding()' is enabled.
What are Matrix parameters in Angular routing?
Matrix parameters are semi-colon separated key-value pairs (e.g. /products;color=red;size=large) attached directly to a specific URL path segment. They are useful for scoping optional state to a particular route hierarchy without polluting global query strings.
Related Articles
How to Use ng-template, ng-container, and ng-content in Angular (Complete Guide)
Learn the exact differences between ng-template, ng-container, and ng-content in Angular, how to pass context with ngTemplateOutlet, multi-slot projection, and how they evolve with modern @if, @for, and @defer control flow.
How to Share Data Between Components in Angular (Signals & Services)
Learn the best patterns to share data between Angular components. Compare legacy Input/Output decorators and BehaviorSubjects with modern Signals, input(), output(), model(), and Signal-based services.
Integrating Third-Party Scripts and CSS into Your Angular App
Learn how to properly integrate third-party JavaScript libraries, CSS stylesheets, and Web Components into Angular using angular.json bundles, dynamic script loaders, and SSR-safe afterNextRender.
Share this article
If you found this guide helpful, consider sharing it with your team or fellow developers.
Real Software. Real Lessons.
I share the lessons I learned the hard way, so you can either avoid them or be ready when they happen.
Join 13,800+ developers and readers.
No spam ever. Unsubscribe at any time.