TanStack Pacer
A type-safe timing and scheduling library for debouncing, throttling, rate limiting, queuing, and batching function execution, with sync and async variants.
Repository Health
Technical Analysis
TanStack Pacer is a lightweight execution-control library that gives JavaScript and TypeScript applications a consistent, type-safe way to manage how often and in what order functions run. Rather than reaching for ad-hoc setTimeout wrappers or hand-rolled queue classes scattered across a codebase, teams get a single set of primitives — debounce, throttle, rate limiter, queuer, batcher, and retryer — each available in a synchronous form and a promise-aware async form with built-in error handling, retries, and abort support.
Every utility is built as a class wrapping a TanStack Store instance, so state (pending status, execution counts, queue contents) is reactive and observable rather than hidden inside closures. That same store is what powers the official React, Solid, Preact, and Angular adapter hooks (useDebouncedCallback, useThrottledValue, useQueuedState, and more), letting the same core logic drive both vanilla JS and framework-bound UI state with fine-grained re-renders.
The queuing utilities in particular go well beyond a simple FIFO: FIFO, LIFO, and priority-queue orderings are all supported, with configurable concurrency, start/stop control, and per-item expiration. The batching utilities let callers group operations by time window, item count, or a custom predicate — useful for coalescing high-frequency events (search-as-you-type, resize handlers, websocket message bursts) into fewer, cheaper downstream calls.
Built and maintained by the TanStack team behind Query, Table, and Router, Pacer follows the same conventions: framework-agnostic core, thin adapter packages per framework, full TypeScript generics for argument and return-type inference, and a devtools panel for inspecting live debouncer/throttler/queue state during development.
What You Get
- Debounce & Throttle - synchronous and async variants with leading/trailing edge control, each backed by a reactive state object (
isPending,executionCount, etc.) rather than opaque closures. - Rate Limiting - fixed-window and sliding-window rate limiters that cap how often a function can fire over a period, with async error/success/settled callbacks.
- Queuing - FIFO, LIFO, and priority queues with configurable concurrency, wait times, start/stop control, and automatic expiration of stale items.
- Batching - group calls by elapsed time, item count, or a custom condition to reduce many small operations into fewer batched executions.
- Async Retryer - a dedicated async utility for retrying failing operations with configurable retry counts and abort support.
- Framework Adapters - React, Solid, Preact, and Angular hooks (
useDebouncedCallback,useThrottledValue,useQueuedState) built on the same core classes. - Devtools Panel - a dedicated
@tanstack/pacer-devtoolspackage for inspecting live debouncer/throttler/queuer state during development. - Full Type Safety - generics preserve the wrapped function’s argument and return types throughout every utility.
Common Use Cases
- Search-as-you-type - debounce keystroke-driven API calls so a request only fires after the user pauses typing.
- Scroll and resize handlers - throttle high-frequency DOM events so expensive layout or fetch logic only runs at a bounded rate.
- Third-party API rate limiting - wrap outbound calls to a rate-limited external API in a
RateLimiterto stay under quota without manual token-bucket code. - Background job fan-out - use a priority
Queuerwith a concurrency cap to process a burst of async tasks (uploads, webhook deliveries) in a controlled order. - Websocket/event coalescing - batch a stream of frequent websocket messages into periodic bulk UI updates instead of re-rendering on every message.
- Flaky network calls - wrap a fetch in the async retryer to automatically retry transient failures with backoff before surfacing an error.
Under The Hood
Architecture
Each utility (Debouncer, Throttler, RateLimiter, Queuer, Batcher, and their async counterparts) is a self-contained class in packages/pacer/src that wraps a Store instance from @tanstack/store as its single source of truth, with private #setState methods normalizing derived fields like status on every update and an emitChange call feeding an internal devtools event bus (event-client.ts). Public methods (maybeExecute, flush, cancel, reset) are defined as class field arrow functions so they can be destructured and passed as callbacks without losing this binding, and a plain-function wrapper (debounce, throttle, etc.) sits on top of each class for callers who don’t need direct access to the instance. Framework adapters in sibling packages (react-pacer, solid-pacer, preact-pacer, angular-pacer) subscribe to the same store to expose reactive hooks, so the core has zero framework dependency and all UI-binding logic lives at the edges.
Tech Stack
The core @tanstack/pacer package is pure TypeScript with two runtime dependencies: @tanstack/store for reactive state and @tanstack/devtools-event-client for the devtools event bus. The monorepo is managed with pnpm workspaces and Nx for task orchestration/caching, built per-package with tsdown (emitting unbundled ESM and CJS output plus .d.ts files validated by publint), and tested with Vitest using fake timers to deterministically exercise time-based behavior. Framework adapter packages add their respective peer dependencies (React, Solid, Preact, Angular) on top of the same core.
Code Quality
Every source file under packages/pacer/src has a matching test file under packages/pacer/tests, and the test suite (~9,700 lines) is actually larger than the source it covers (~6,500 lines), exercising basic behavior, edge cases, leading/trailing combinations, and cancellation paths with Vitest’s fake-timer APIs. The root tsconfig.json enables strict, noUncheckedIndexedAccess, noUnusedLocals, and noImplicitReturns, and CI (test:pr) runs ESLint, type-checking, sherif (monorepo dependency consistency), knip (unused exports), doc-link verification, and a full build on every pull request, with an autofix bot applying formatting and doc regeneration automatically.
What Makes It Unique Most debounce/throttle utilities on npm are single-purpose and stateless; Pacer instead treats timing control as a small family of related primitives (debounce, throttle, rate limit, queue, batch, retry) sharing one reactive-state model, so switching from a sync debouncer to an async one with retry/abort support is a drop-in change rather than a rewrite. Exposing that state through TanStack Store — the same primitive underlying TanStack Query’s cache — lets framework adapters get fine-grained reactive re-renders essentially for free, and gives every utility a built-in devtools story without extra instrumentation code.
Used by 5 apps in this directory
LiteLLM
AI Development · Developer Tools
Open source AI gateway and Python SDK that gives you one OpenAI-compatible interface to call 100+ LLM providers, with built-in routing, cost tracking, guardrails, and virtual keys.
Mistle
AI Agents · Developer Tools
Self-hostable platform for running autonomous coding agents in isolated, credentialless sandboxes with brokered credentials, reusable snapshots, and event-driven triggers.
Rivet
AI Agents · Developer Tools
Stateful actors as a primitive for AI agents, real-time collaboration, and durable execution — with in-memory state, WebSockets, queues, and scheduling built in.
Sentry
Security · Developer Tools · Monitoring
Developer-first error tracking and performance monitoring platform with AI-powered root-cause analysis across 20+ languages and frameworks.
Sentry
Security · Developer Tools · Monitoring
Developer-first error tracking and performance monitoring platform with AI-powered root-cause analysis across 20+ languages and frameworks.