Angular 22 has debounced() for the classic search box that should wait until the user stops typing. Until now that meant RxJS: debounceTime, switchMap and a subscription. debounced() takes a signal and a delay and returns a resource whose value settles once the input has been quiet that long. It comes from @angular/core and has been experimental since 22.0.

Search-as-you-type with httpResource

The official guide pairs it with resource(). With httpResource I'd write it like this, returning undefined for an empty query so that no request goes out:

query = signal('');
search = debounced(this.query, 300);

results = httpResource<Item[]>(() => {
  const q = this.search.value();
  return q ? `/api/search?q=${encodeURIComponent(q)}` : undefined;
});
<input #box (input)="query.set(box.value)" />
@for (item of results.value() ?? []; track item.id) {
  <li>{{ item.name }}</li>
}

Every keystroke updates query. While the timer runs, search.status() is 'loading' and search.value() still returns the last settled text, so the URL only changes once typing stops. If the query changes again before the wait is over, the timer starts again from zero.

Details worth knowing

The first value isn't delayed: the resource starts out 'resolved' with the signal's current value. That's why the empty-query check matters. Without it, the page sends ?q= as soon as it loads.

debounced() needs an injection context, such as a field initializer, or an injector in its options. The only other option is equal, which defaults to Object.is. Instead of a number, the wait can also be a function that returns a promise, for example to wait longer while the query is still short. When the injector is destroyed, a pending timer is cancelled.

If you're on 22.0 or 22.1, update to at least 22.1.5, which fixed debounce timers that were never cleared. And it's experimental, which under Angular's release policy means it can change in any release, even a patch. For a search box that's a risk I'd take.

Sources

I post short notes like this on LinkedIn.

Need help with this?

A search box with "angu" typed: four keystrokes, then 300 ms of quiet, then one GET request for ?q=angu. Below, code with query = signal(''), search = debounced(this.query, 300) and an httpResource that reads search.value().

Have an Angular app that needs a senior hand?

Tell me about your app and what is slowing your team down. We go through it on a call and you leave with concrete next steps.