How to Use Query Parameters in Angular

Query parameters in Angular allow optional application state to be passed through the URL without changing the route that Angular matches. Unlike route parameters, which form part of the route path and must exist before a route can activate, query parameters follow the ? character and may be omitted without affecting navigation. They are commonly used to represent filter options, pagination details, and sorting preferences in Angular applications.

In this tutorial, you will work with a product-listing example to understand how to create query parameters with Router.navigate and RouterLink, retain or combine parameters across navigations with queryParamsHandling, and retrieve their values inside a component through ActivatedRoute. You will also compare snapshot-based and observable-based reading methods and learn how Angular 16 and newer versions can map query parameters directly to component inputs without requiring ActivatedRoute injection.

Key Takeaways

  • Query parameters are optional, follow ? in a URL, and do not participate in route matching.
  • Use queryParams with Router.navigate or [queryParams] with RouterLink to add parameters to a navigation destination.
  • queryParamsHandling: 'preserve' transfers the existing query string to the next route without changing it, while 'merge' combines current and new parameters and replaces values when keys are duplicated.
  • If queryParamsHandling is not specified, Angular removes previously existing query parameters during the next navigation.
  • ActivatedRoute.snapshot.queryParams should only be used when the component is guaranteed to be destroyed and created again for every navigation.
  • Use ActivatedRoute.queryParams or queryParamMap subscriptions when Angular keeps the same component instance active while navigation changes the query string.
  • queryParamMap.get('key') returns null when the parameter does not exist, so apply a null check or a ?? fallback before using the result.
  • Angular 16 and later support withComponentInputBinding(), which maps query parameters to @Input() properties without injecting ActivatedRoute.
  • Query parameters work well for search filters, pagination, and application state that should remain bookmarkable or shareable through a URL.

Prerequisites

To complete this tutorial, you will need:

  • Basic knowledge of Angular Router, RouterLink, and ActivatedRoute.
  • Angular 16 or a newer version.

What Are Query Parameters in Angular?

Query parameters are key-value pairs placed after a ? in a URL. When more than one pair is present, the pairs are separated with &. In the following URL, order and price-range are query parameters:

http://localhost:4200/products?order=popular&price-range=not-cheap

Angular Router automatically interprets these pairs and makes them available through ActivatedRoute. Because query parameters are not included in the route path itself, Angular continues to match /products regardless of the values contained in the query string.

Query Parameters Compared with Route Parameters

The following comparison shows how query parameters differ from route parameters and helps determine which option is appropriate for a particular requirement.

Feature Query Parameters Route Parameters
URL appearance Placed after ?, for example /products?order=popular Included in the path, for example /products/123
Required No, they are always optional Yes, they must exist for the route to match
Route matching They do not change route matching They must be present before the route can activate
Navigation API queryParams inside NavigationExtras A path-array value passed to Router.navigate
RouterLink binding [queryParams]="{ key: value }" A path value such as [routerLink]="['/products', id]"
Typical use case Filters, sorting, pagination, and shareable state Resource identifiers such as IDs and slugs

The main difference is that route parameters identify the resource that should be loaded, whereas query parameters adjust the way that resource is displayed, filtered, or ordered.

When to Use Query Parameters

Query parameters are suitable when a value is optional, meaning the route should work whether the value exists or not. They are also useful when a view should be shareable or bookmarkable so that another user can open the same filtered state. In addition, query parameters can be carried between routes when a value must remain available during navigation.

Typical examples include search pages such as /search?q=angular, paginated lists such as /products?page=2&pageSize=20, and filter interfaces such as /items?category=books&sort=price.

Using Query Parameters with Router.navigate

To include query parameters during programmatic navigation, pass a queryParams object in the NavigationExtras argument supplied to Router.navigate. The NavigationExtras type, imported from @angular/router, supports properties including queryParams, queryParamsHandling, fragment, and other navigation settings.

The following example opens /products and adds order=popular to the URL:

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

@Component({ ... })
export class ProductListComponent {
  constructor(private router: Router) {}

  goProducts() {
    this.router.navigate(
      ['/products'],
      { queryParams: { order: 'popular' } }
    );
  }
}

This navigation creates a URL similar to:

Output

http://localhost:4200/products?order=popular

To include several query parameters, add additional properties to the queryParams object. The next example supplies both order and price-range:

goProducts() {
  this.router.navigate(
    ['/products'],
    { queryParams: { order: 'popular', 'price-range': 'not-cheap' } }
  );
}

The resulting URL is:

Output

http://localhost:4200/products?order=popular&price-range=not-cheap

Preserving or Merging Parameters with queryParamsHandling

Angular normally removes all query parameters when navigation moves to another route. The queryParamsHandling property in NavigationExtras changes this default behavior. The value 'preserve' retains the complete current query string, while 'merge' combines existing parameters with the new parameters provided for the navigation.

Preserving Existing Query Parameters

Use 'preserve' when a related destination should receive the current query context without adding or modifying any values. In the following example, a user currently visiting /products?order=popular navigates to /users while keeping the order parameter:

goUsers() {
  this.router.navigate(
    ['/users'],
    { queryParamsHandling: 'preserve' }
  );
}

The destination URL still contains the original parameter:

Output

http://localhost:4200/users?order=popular

Merging Existing and New Query Parameters

Use 'merge' when a navigation should add or replace selected parameters while leaving all other existing values intact. The next example adds a filter parameter while retaining the existing order parameter:

goUsers() {
  this.router.navigate(
    ['/users'],
    {
      queryParams: { filter: 'new' },
      queryParamsHandling: 'merge'
    }
  );
}

The combined URL includes both values:

Output

http://localhost:4200/users?order=popular&filter=new

Note: The preserveQueryParams setting was deprecated in Angular 4 and removed in Angular 8. Use queryParamsHandling: 'preserve' instead.

A common reason to use 'merge' is a filter process spread across several steps. A user might select a category in one component and choose a price range in another. Each navigation can merge the latest selection into the query string without removing values selected during an earlier step.

Using Query Parameters with RouterLink

The RouterLink directive provides the same queryParams and queryParamsHandling options available through Router.navigate. Bind an object to [queryParams] to define parameters and use the queryParamsHandling attribute to determine how current parameters should be treated.

To define a query parameter in a template, bind [queryParams] to an object literal:

<a
  [routerLink]="['/products']"
  [queryParams]="{ order: 'popular' }"
>
  Products
</a>

To retain or combine parameters during template-based navigation, include queryParamsHandling. The following link merges the current query string with a new filter value:

<a
  [routerLink]="['/users']"
  [queryParams]="{ filter: 'new' }"
  queryParamsHandling="merge"
>
  Users
</a>

In this example, queryParamsHandling receives a fixed string rather than a bound expression, so square brackets are not required.

Accessing Query Parameter Values

The ActivatedRoute service offers two main methods for reading query parameters inside a component. A snapshot provides the parameter state captured when the component is initialized, while observables emit new values whenever the URL changes. Angular 16 also introduced an additional option that automatically maps route values to component inputs.

Reading Parameters with ActivatedRoute.snapshot.queryParams

ActivatedRoute.snapshot is an ActivatedRouteSnapshot representing the route state at the time the component was created. Accessing snapshot.queryParams is synchronous and does not require a subscription:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({ ... })
export class ProductComponent implements OnInit {
  order: string | null = null;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    // Read the value once when the component is created
    this.order = this.route.snapshot.queryParams['order'] ?? null;
    console.log(this.order);
  }
}

When the URL is http://localhost:4200/products?order=popular, the code logs:

Output

The snapshot method is appropriate when every navigation destroys the current component and creates a new instance. Its disadvantage appears when the same component remains active while only the query string changes. For example, if a user changes a sorting option without leaving the page, the existing snapshot does not receive the updated value.

Reading Parameters with the queryParams Observable

Subscribing to ActivatedRoute.queryParams provides a stream that emits the complete query-parameter object each time the URL changes while the component remains active. This method is appropriate for components that Angular may reuse between navigations:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { filter } from 'rxjs';

@Component({ ... })
export class ProductComponent implements OnInit {
  order: string = '';

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    this.route.queryParams
      .pipe(filter(params => params['order'] !== undefined))
      .subscribe(params => {
        this.order = params['order'];
        console.log(this.order);
      });
  }
}

For the URL http://localhost:4200/products?order=popular, the subscription outputs:

Output

The queryParams observable is the usual option when a component must respond to parameter updates without being recreated. When individual typed access and repeated parameter values are important, queryParamMap provides a more suitable interface.

Reading Parameters with queryParamMap

ActivatedRoute.queryParamMap returns an observable containing a ParamMap. This object provides accessor methods for retrieving values. It is often preferred when reading individual parameters because ParamMap.get() returns null, rather than undefined, for a missing key. The ParamMap.getAll() method also supports parameters that occur more than once.

For example, the URL /products?tag=angular&tag=router contains two values for the tag parameter. Calling getAll('tag') returns both values in an array.

For the URL http://localhost:4200/products?order=popular&filter=new, each parameter can be retrieved with paramMap.get():

this.route.queryParamMap.subscribe(params => {
  const order = params.get('order');      // 'popular'
  const filterVal = params.get('filter'); // 'new'
  const keys = params.keys;               // ['order', 'filter']
  console.log(order, filterVal, keys);
});

The subscription logs the sorting value, filter value, and all active parameter names:

Output

popular new ['order', 'filter']

queryParamMap is a reliable long-term approach for reading Angular query parameters because get() and getAll() make missing values explicit and simplify checks for parameter availability.

Snapshot or Observable: Which Method Should You Choose?

Use snapshot.queryParams or snapshot.queryParamMap when the component is always created again for each navigation and does not need to respond to URL changes during its current lifecycle. This approach is straightforward and does not require additional reactive logic.

Subscribe to queryParams or queryParamMap when Angular may reuse the component while the query string changes. A sortable list page is a typical example. The user changes the sorting order and the URL updates, but Angular can leave the existing component instance in place. In that situation, a snapshot continues to contain the original value and may cause outdated information to remain visible. An observable resolves the problem by responding to every URL update while the component is active.

Modern Query Parameter Binding with @Input()

Angular 16 introduced withComponentInputBinding(), a Router feature that automatically maps query parameters and other route values to @Input() properties on a routed component. For simple read operations, enabling this feature removes the need to inject ActivatedRoute.

Enable the feature by including withComponentInputBinding() in the provideRouter() call inside app.config.ts:

// app.config.ts for an Angular 16+ standalone application
import { ApplicationConfig } from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes, withComponentInputBinding())
  ]
};

After the feature has been enabled, create an @Input() property with the same name as the query-parameter key:

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-product',
  standalone: true,
  template: `<p>Order: {{ order }}</p>`,
})
export class ProductComponent {
  // Angular automatically maps the ?order=... parameter to this property
  @Input() order: string | undefined;
}

Angular assigns the query-string value to order whenever the component activates or the parameter changes. In Angular 16 and newer projects that use standalone components, this provides a clean option for read-only query-parameter access.

Converting queryParamMap to a Signal

When a reactive signal is required instead of an @Input(), the queryParamMap observable can be converted with toSignal() from @angular/core/rxjs-interop. The inject() function, available since Angular 14, also allows ActivatedRoute to be accessed without constructor injection:

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

@Component({
  selector: 'app-product',
  standalone: true,
  template: `<p>Order: {{ order() }}</p>`,
})
export class ProductComponent {
  private route = inject(ActivatedRoute);

  // A read-only signal that updates whenever ?order= changes
  readonly order = toSignal(
    this.route.queryParamMap.pipe(map(p => p.get('order')))
  );
}

The order property is a read-only signal that works with Angular’s signal-oriented change detection introduced in Angular 16. It updates automatically whenever the query parameter changes, and the template renders the latest value without requiring a manual subscription.

Real-World Query Parameter Use Cases

Knowing when query parameters are appropriate is just as important as understanding the API. Three common patterns are found in almost every Angular application.

Search and Filter Pages

Search and filter interfaces can store their active state in the URL, allowing users to bookmark or share a particular view. A URL such as /items?category=books&sort=price can be opened in another browser tab and reproduce the same filtered result. Without query parameters, the state remains only in component memory and disappears after a page reload.

Pagination

Pagination can use page and pageSize parameters so users can navigate directly to a particular page and return to the same position after refreshing the browser. A typical format is /products?page=2&pageSize=20. When pageSize is missing, the application can apply an appropriate default value.

Multi-Step Workflows

Multi-step processes can use queryParamsHandling: 'merge' to collect selections across several navigations without relying on a shared service. For example, a user may choose a date range on one page and a location on the next. Both selections can remain in the URL when every navigation uses 'merge'.

Frequently Asked Questions

1. How Do You Pass Query Parameters in Angular Routing?

Query parameters can be passed in Angular by supplying a queryParams object within the NavigationExtras argument used by Router.navigate. For example, the following navigation adds an order parameter:

this.router.navigate(['/products'], { queryParams: { order: 'popular' } });

This produces a URL such as /products?order=popular.

Inside a template, the same result can be achieved with the [queryParams] binding on RouterLink:

<a [routerLink]="['/products']" [queryParams]="{ order: 'popular' }">
  Products
</a>

Both techniques append optional query parameters to the route and make it possible to represent shareable application state in the URL.

2. How Do You Add a Query Parameter to a RouterLink in Angular?

To include query parameters in a template, bind the [queryParams] input of an anchor or another element using RouterLink to an object containing the required parameter names and values:

<a [routerLink]="['/products']" [queryParams]="{ order: 'popular' }">
  Products
</a>

For dynamically generated parameters, bind [queryParams] to a component property such as filterOptions. The values can then change when users interact with the application or when application state is updated.

For example:

// In the component class
filterOptions = { order: 'popular', 'price-range': 'not-cheap' };

<a [routerLink]="['/products']" [queryParams]="filterOptions">
  Filtered Products
</a>

3. What Is the Difference Between Query Parameters and Route Parameters in Angular?

Route parameters are placed directly within URL path segments. For example, /products/:id defines id as a route parameter. This value is required for the route to match and must be included in the URL. Route parameters generally identify a specific resource, such as an individual product ID.

Query parameters appear after a ? and are represented as key-value pairs. For example, /products?order=popular uses a query parameter to describe the sorting order. Query parameters remain optional and do not determine which route Angular selects. They are generally used to control how a resource is displayed, filtered, or sorted.

Query parameters may therefore be included or left out without preventing the route from matching.

4. How Do You Pass Multiple Query Parameters in Angular?

Any number of query parameters can be included by adding more key-value pairs to the parameter object. For example, the following code supplies both order and price-range.

Programmatic navigation:

this.router.navigate(['/products'], {
  queryParams: { order: 'popular', 'price-range': 'not-cheap' }
});

Template navigation:

<a
  [routerLink]="['/products']"
  [queryParams]="{ order: 'popular', 'price-range': 'not-cheap' }"
>
  Products
</a>

Both approaches produce a URL similar to:

/products?order=popular&price-range=not-cheap

This structure can support any number of optional parameters.

5. What Is the Difference Between snapshot.queryParams and the queryParams Observable?

When snapshot.queryParams is used, Angular reads the query parameters once, at the time the component is first created. This technique is best suited to situations where navigation always destroys and creates the component again.

For example:

const order = this.route.snapshot.queryParams['order'];

Subscribing to queryParams as an observable allows the component to respond when query parameters change while the component remains active. Angular does not necessarily destroy and recreate a component when only query-parameter values change. This method is therefore necessary for interactive interfaces involving filters, sorting, or pagination.

For example:

this.route.queryParams.subscribe(params => {
  this.order = params['order'];
});

Use a snapshot when the route always creates a fresh component instance. Use an observable subscription when the component can remain active across URL changes and must continue receiving current parameter values.

6. What Does queryParamsHandling: ‘preserve’ Do Compared with ‘merge’?

With queryParamsHandling: 'preserve', Angular transfers the entire current query string to the destination route without changing it. For example, navigating away from /products?order=popular with 'preserve' can produce /users?order=popular.

With queryParamsHandling: 'merge', Angular combines existing parameters with any new parameters provided for the navigation. When a new parameter uses the same key as an existing parameter, the new value replaces the old one. For example, navigating from /products?order=popular to /users with queryParams: { filter: 'new' } and queryParamsHandling: 'merge' produces /users?order=popular&filter=new.

The following table summarizes the difference:

Option Does It Retain Existing Parameters? Can It Add or Update Parameters? Typical Use Case
preserve Yes No Keeping the complete existing context while moving between pages
merge Yes, unless a duplicate key is replaced Yes, new and existing keys can be assigned Adding new values or updating selected parameters during navigation

7. How Do You Read a Specific Query Parameter in an Angular Component?

Angular provides several ways to read an individual query parameter, depending on the required behavior and the Angular version in use.

To react whenever query parameters change, subscribe to the ActivatedRoute.queryParamMap observable. This method reliably delivers updated values:

this.route.queryParamMap.subscribe(params => {
  const value = params.get('order'); // A string value, or null when the parameter is absent
});

When the parameter only needs to be read once during component initialization, use ActivatedRoute.snapshot.queryParamMap:

const value = this.route.snapshot.queryParamMap.get('order');

In Angular 16 or later, standalone applications configured with withComponentInputBinding() can assign query-parameter values automatically to component input properties:

@Input() order: string | undefined;

Angular then updates the order input whenever the corresponding URL parameter changes.

The raw queryParams object returns undefined when a key is missing, while queryParamMap.get() returns null. Check for the appropriate missing value or apply the ?? nullish-coalescing operator.

8. Can Query Parameters Cause a Route Mismatch or Navigation Failure in Angular?

No. Query parameters do not affect the route selected by Angular. Angular uses only the URL path to identify and activate a route. Values following the path, such as ?key=value, are ignored during route matching.

Angular therefore matches the same route for /products, /products?order=popular, and /products?foo=bar&baz=qux. Whether query parameters are present or absent does not change which route is activated.

Component logic must still account for parameters that are missing or contain unexpected values. Before using a query parameter, verify that it is not null or undefined to prevent runtime problems or unintended application behavior.

For example:

const value = this.route.snapshot.queryParamMap.get('order');

if (value !== null) {
  // Use the value
}

This pattern allows the application to handle optional or missing query parameters safely.

Conclusion

In this tutorial, you used queryParams and queryParamsHandling with both Router.navigate and RouterLink to define and manage Angular query parameters. You also retrieved parameter values through ActivatedRoute.snapshot, the queryParams observable, and queryParamMap. In addition, you learned how withComponentInputBinding() in Angular 16 and later simplifies parameter access for standalone components and how toSignal() connects query parameters to Angular’s signal system.

Source: digitalocean.com

Create a Free Account

Register now and get access to our Cloud Services.

Posts you might be interested in: