npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@gitesh08/signal-utils

v1.0.4

Published

Minimal Angular signal utilities: debounce, throttle, and merge signals without RxJS bloat. Lightweight, tree-shakable, zoneless-friendly.

Readme

@gitesh08/signal-utils

Minimal, tree-shakable Angular signal utilities for debounce, throttle, and merge operations. Built for modern Angular applications without RxJS overhead.

npm version Angular License

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:

Open in StackBlitz

Installation

npm install @gitesh08/signal-utils

Requirements:

  • 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 debouncedSignal and throttledSignal throw 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 -- --watch

Tests 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.