Debounced

Creates a debounced signal that delays value updates until a specified time has passed without changes.

Loading demo...

Usage

Writable signal

Creates a new writable signal where both set() and update() calls are debounced:

angular-ts
import { Component, effect } from '@angular/core';
import { debounced } from '@signality/core';

@Component({
  template: `
    <input [(ngModel)]="value" />
  `,
})
export class DebouncedInput {
  readonly value = debounced('', 300); 

  constructor() {
    effect(() => {
      console.log('Debounced:', this.value());
    });
  }
}

Computed signal

Wraps an existing signal with debounced behavior, returning a readonly signal:

angular-ts
import { Component, signal, effect } from '@angular/core';
import { debounced } from '@signality/core';

@Component({ /* ... */ })
export class DebouncedInput {
  readonly rawValue = signal('');
  readonly debouncedValue = debounced(this.rawValue, 300); 

  constructor() {
    effect(() => {
      console.log('Debounced:', this.debouncedValue());
    });
  }
}

Parameters

ParameterTypeDescription
sourceSignal<T> or TSource signal to debounce, or initial value for writable signal
timeMsMaybeSignal<number>Debounce delay in milliseconds
optionsDebouncedOptions<T>Optional configuration (see Options below)

Options

The DebouncedOptions<T> extends CreateSignalOptions<T> and WithInjector:

OptionTypeDescription
equalValueEqualityFn<T>Custom equality function (see more)
debugNamestringDebug name for the signal (development only)
injectorInjectorOptional injector for DI context

Return Value

  • When passed a signal → returns Signal<T> (readonly)
  • When passed a value → returns WritableSignal<T>

Examples

Search with API Call

angular-ts
import { Component } from '@angular/core';
import { httpResource } from '@angular/common/http';
import { debounced } from '@signality/core';

@Component({
  template: `
    <input 
      placeholder="Search..." 
      (input)="query.set($event.target.value)" 
    />
    <ul>
      @if (resource.value(); as results) {
        @for (result of results; track result.id) {
          <li>{{ result.name }}</li>
        }
      }
    </ul>
  `,
})
export class SearchInput {
  readonly query = debounced('', 400);
  readonly resource = httpResource(() => `/api/search?q=${this.query()}`);
}

Dynamic delay

The delay can be a signal, allowing runtime changes:

angular-ts
import { Component, computed, signal } from '@angular/core';
import { debounced } from '@signality/core';

@Component({
  template: `
    <input (input)="value.set($event.target.value)" />
    <label>
      <input 
        type="checkbox" 
        (change)="fastMode.set($any($event.target).checked)" 
      />
      Fast mode
    </label>
  `,
})
export class WithDynamicDelay {
  readonly fastMode = signal(false);
  readonly delay = computed(() => this.fastMode() ? 100 : 500); 
  readonly value = debounced('', this.delay); 
}

SSR Compatibility

Debounce timers are not started on the server — the initial value is returned immediately.

Type Definitions

typescript
type DebouncedOptions<T> = CreateSignalOptions<T> & WithInjector;

// Overload 1: Computed from signal (readonly)
function debounced<S extends Signal<any>>(
  source: S,
  timeMs: MaybeSignal<number>,
  options?: DebouncedOptions<SignalValue<S>>
): Signal<SignalValue<S>>;

// Overload 2: Writable signal from value
function debounced<V>(
  value: V,
  timeMs: MaybeSignal<number>,
  options?: DebouncedOptions<V>
): WritableSignal<V>;
Edit this page on GitHub Last updated: Mar 19, 2026, 23:28:23