Reader Stacks

How to Get Query String Parameters in Angular

queryParamMap is an Observable, not a plain object — subscribing to it, not just reading it once, is what keeps a component correctly in sync when only the query string changes on the same route.

How to Get Query String Parameters in Angular

Reading query string parameters (like ?category=electronics&sort=price) in Angular goes through ActivatedRoute's queryParamMap, which — being an Observable rather than a plain snapshot — needs to be subscribed to, not just read once, to stay correctly in sync.

Reading a query param via subscription

constructor(private route: ActivatedRoute) {}

ngOnInit(): void {
    this.route.queryParamMap.subscribe(params => {
        this.category = params.get('category');
        this.sortBy = params.get('sort') ?? 'name';
    });
}

Subscribing (rather than reading a one-time snapshot) is what correctly reacts to a query string change even when Angular reuses the same component instance for the new URL — a common scenario when only the query params change while the route's path stays the same.

Reading a one-time snapshot instead, when reactivity isn't needed

ngOnInit(): void {
    this.category = this.route.snapshot.queryParamMap.get('category');
}

route.snapshot reads the query params exactly once, at the moment this code runs — appropriate only when the component is guaranteed to be freshly created for every URL change (rather than reused), since it won't pick up a subsequent in-place query param change on its own.

Reading multiple params, and handling an array-valued param

this.route.queryParamMap.subscribe(params => {
    this.category = params.get('category');
    this.tags = params.getAll('tag'); // ?tag=sale&tag=new → ['sale', 'new']
});

getAll(), rather than get(), is needed when the same query parameter key can legitimately appear multiple times in the URL — get() alone would only return the first matching value and silently drop the rest.

Updating the query string without a full navigation

constructor(private route: ActivatedRoute, private router: Router) {}

updateSort(sortBy: string): void {
    this.router.navigate([], {
        relativeTo: this.route,
        queryParams: { sort: sortBy },
        queryParamsHandling: 'merge',
    });
}

queryParamsHandling: 'merge' combines the new query param with any existing ones already in the URL, rather than replacing the entire query string — without it, updating just the sort order would wipe out any other active filters (like category) already present in the URL.

Combining route params and query params together

// URL: /products/electronics?sort=price
this.route.paramMap.subscribe(params => {
    this.categorySlug = params.get('categorySlug'); // route param
});

this.route.queryParamMap.subscribe(params => {
    this.sortBy = params.get('sort'); // query param
});

Route params (paramMap, part of the URL's path structure) and query params (queryParamMap, the part after the ?) are read through two genuinely separate observables — a component using both needs to subscribe to each independently.