@gitesh08/signal-utils
v1.0.4
Published
Minimal Angular signal utilities: debounce, throttle, and merge signals without RxJS bloat. Lightweight, tree-shakable, zoneless-friendly.
Maintainers
Readme
@gitesh08/signal-utils
Minimal, tree-shakable Angular signal utilities for debounce, throttle, and merge operations. Built for modern Angular applications without RxJS overhead.
Why This Library?
Working with Angular signals for search inputs, scroll handlers, or form state? You probably need debouncing or throttling. This library gives you exactly that—nothing more, nothing less.
What you get:
- Debounced signals for search boxes and text inputs
- Throttled signals for scroll, resize, and mouse events
- Signal merging for combining multiple reactive values
- Type-safe, validated inputs (throws on negative delays)
- Works in zoneless Angular apps
- Zero dependencies except
@angular/core
What you don't get:
- Extra bloat
- RxJS as a peer dependency
- Complicated APIs
Live Interactive Demo
Try debounce, throttle & merge in your browser – edit/fork freely:
Installation
npm install @gitesh08/signal-utilsRequirements:
- Angular 17 or higher
- TypeScript 5.5+
Quick Start
Debounced Search
Stop hammering your API on every keystroke. Debounce waits for the user to stop typing before updating.
import { Component, signal, effect } from '@angular/core';
import { debouncedSignal } from '@gitesh08/signal-utils';
@Component({
selector: 'app-search',
standalone: true,
template: `
<input type="text" [(ngModel)]="searchTerm" placeholder="Search products..." />
<p *ngIf="debouncedTerm()">Searching: {{ debouncedTerm() }}</p>
`
})
export class SearchComponent {
searchTerm = signal('');
debouncedTerm = debouncedSignal(this.searchTerm, 400);
constructor() {
effect(() => {
const term = this.debouncedTerm();
if (term.length > 2) {
// Make your API call here
console.log('Fetching results for:', term);
}
});
}
}Throttled Window Events
Limit how often expensive operations run during rapid events like scrolling or resizing.
import { Component, signal, effect, OnInit, OnDestroy } from '@angular/core';
import { throttledSignal } from '@gitesh08/signal-utils';
@Component({
selector: 'app-resize-detector',
standalone: true,
template: `<div>Window: {{ width() }}px × {{ height() }}px</div>`
})
export class ResizeDetectorComponent implements OnInit, OnDestroy {
private rawWidth = signal(window.innerWidth);
private rawHeight = signal(window.innerHeight);
width = throttledSignal(this.rawWidth, 200);
height = throttledSignal(this.rawHeight, 200);
private listener = () => {
this.rawWidth.set(window.innerWidth);
this.rawHeight.set(window.innerHeight);
};
ngOnInit() {
window.addEventListener('resize', this.listener);
}
ngOnDestroy() {
window.removeEventListener('resize', this.listener);
}
}Merge Multiple Signals
Combine related signals into a single reactive object. Great for forms or grouped state.
import { Component, signal } from '@angular/core';
import { mergeSignals } from '@gitesh08/signal-utils';
@Component({
selector: 'app-user-form',
standalone: true,
template: `
<input [(ngModel)]="firstName" placeholder="First name" />
<input [(ngModel)]="lastName" placeholder="Last name" />
<input [(ngModel)]="email" placeholder="Email" />
<pre>{{ userData() | json }}</pre>
`
})
export class UserFormComponent {
firstName = signal('');
lastName = signal('');
email = signal('');
userData = mergeSignals({
firstName: this.firstName,
lastName: this.lastName,
email: this.email
});
// userData() returns: { firstName: '...', lastName: '...', email: '...' }
}API Reference
| Function | Description | Parameters | Returns |
|----------|-------------|------------|---------|
| debouncedSignal<T> | Delays signal updates until activity stops | source: Signal<T>, delay?: number (default: 300ms) | Signal<T> (readonly) |
| throttledSignal<T> | Limits signal updates to once per interval | source: Signal<T>, interval?: number (default: 300ms) | Signal<T> (readonly) |
| mergeSignals<T> | Combines multiple signals into one object | signals: { [K in keyof T]: Signal<T[K]> } | Signal<T> (computed) |
Error handling:
- Both
debouncedSignalandthrottledSignalthrow an error if delay/interval is negative - All functions must be called within an injection context (component, service, etc.)
Development
# Build the library
ng build signal-utils
# Run tests
npm run test:lib
# Watch mode during development
npm run test:lib -- --watchTests are written with Vitest and use Angular's TestBed for proper signal context.
Contributing
Found a bug? Have an idea?
Please go through CONTRIBUTING.md before creating issue or PR.
License
MIT © Gitesh Mahadik
Built with signals, for signals.
