Library / SDK
TanStack/pacer avatar
TanStack/pacer

TanStack Pacer: one library for debounce, throttle, rate limit, queue and batch

Utilities for debouncing, throttling, rate limiting, and queueing. Non-async and Async.

793 stars68 forksTypeScriptMIT

At a glance

What is it?
TanStack Pacer collects the timing utilities that every application ends up writing by hand, and it makes a deliberate choice about the axes: every utility exists in a synchronous and an asynchronous flavour, and there are framework adapters for ten frameworks including two that still need a contributor. It is a client-side library first, and the README says so.
Who is it for?
Adopt TanStack Pacer if you are writing a TypeScript front end and want debouncing, throttling, rate limiting, queuing and batching that behave consistently and can be shared between a sync handler and an async one. Do not adopt it as a server-side rate limiter today, because the README states it is currently mostly a client-side only library even though it is being designed to work on the server as well.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Five timing problems, one library, and a client-side framing

TanStack Pacer is described as a lightweight timing and scheduling library for debouncing, throttling, rate limiting, queuing, and batching, and the package description puts it more plainly as utilities for debouncing, throttling, rate limiting and queueing, non-async and async.

The five utilities are distinct problems that are often confused with each other, and the library keeps them separate. Debouncing delays execution until after a period of inactivity, for when you only care about the last execution in a sequence. Throttling smoothly limits the rate at which a function can fire. Rate limiting limits that rate over a period of time, with fixed or sliding window variations. Queuing runs functions in a specific order. Batching chunks multiple operations into larger batches to reduce total back-and-forth.

There is a note at the top of the README that sets expectations honestly. TanStack Pacer is currently mostly a client-side only library, but it is being designed to be able to potentially be used on the server-side as well. The wording is careful, and it matters: if you are reaching for this to protect a backend endpoint from abuse, the library you are looking at is not the tool for that job yet.

The homepage is tanstack.com/pacer, and the project sits in the same family as the other TanStack libraries, with the same sponsor-driven and multi-package structure.

Every utility exists twice, once synchronous and once asynchronous

The first structural decision is the sync and async split, and it is applied to everything. Debouncing comes as synchronous or asynchronous utilities with promise support and error handling. Throttling likewise. Rate limiting likewise. Queuing and batching sit on the same foundation.

For the async variations there is optional error, success and settled handling, and there is retry and abort support. That is a meaningful design decision rather than a wrapper. An async debounce that only returns a promise has no way to tell you whether the work failed, and one that cannot be aborted still runs after the user has moved on.

The control surface on debounce and throttle is the same in both: control of leading, trailing and enabled options. Leading and trailing are the classic pair that decides whether the function fires at the start of a burst, at the end of it, or both, and the enabled flag lets you turn the behaviour off without tearing down the instance.

For rate limiting, the addition is the window choice. Fixed window means a count resets on a boundary, which is cheaper but lets a caller fire twice the limit across a boundary. Sliding window is fairer to the caller and costs more to compute. Having both in one library saves you from reimplementing whichever one your backend actually enforces.

Queues with ordering, priority, expiry and start/stop control

The queuing section is the most feature-dense part of the README, and it is where the library goes beyond a rate limiter.

You choose from FIFO, LIFO and Priority queue implementations. Processing speed is controlled with configurable wait times or concurrency limits, which is the knob that decides whether the queue drains one item at a time, in parallel up to a cap, or on a fixed delay. Queue execution is managed with start and stop capabilities, so a queue can be paused while the app is backgrounded and resumed later.

Items can be expired from the queue after a configurable duration. That is the feature that turns a queue into something you can point at a network request, because a queued request that will never succeed is worse than one that was dropped, and expiry is how you drop it on purpose.

Batching sits next to it with a similar set of triggers. You can batch by time period, by batch size, by whichever comes first, or by a custom condition to trigger batch executions. The custom condition is what makes batching usable for things other than a straight time window, for instance flushing a batch when the user is about to lose focus.

Taken together, the queue and batch utilities are the part of Pacer that goes well past a debounce helper, and they are also the part most likely to be the reason you reach for the library rather than writing a twenty line setTimeout wrapper.

Ten framework adapters, and two of them are still unclaimed

The framework list is long: React, Preact, Solid, Angular, Vue, Svelte, Lit, Alpine, Ember, and Octane. Each has its own adapter package under the @tanstack scope, and the release list in the repository shows that they are versioned independently, with the Angular adapter at 0.27.0, the Preact adapter at 0.24.2 and the Octane adapter at 0.3.0 on the same day in mid-October 2026.

That independent versioning is a fact worth sitting with. The adapter versions do not move in lockstep, so an application on two frameworks in the same monorepo will see two different numbers.

The README also explains the hook model. Framework adapters provide lifecycle-owned utilities and state and value helpers, and React, Preact and Octane additionally provide callback-only hooks. Multiple layers of abstraction are available depending on the use case. The adapters work with each framework's default state management or with a custom one you prefer.

The two adapters marked as needing attention are Svelte Pacer and Vue Pacer, both listed with a needs a contributor note. That is useful information for someone deciding whether to build on a given adapter, and it is the kind of detail that a documentation site would bury while a README puts it in plain sight.

Under the hood, state is managed with TanStack Store with fine-grained reactivity, and some utilities such as rate limiting and queuing can persist their state to local or session storage. That persistence is what lets a rate limit survive a page reload, which matters if the limit is meant to protect the user rather than the server.

Tree shaking, deep imports and the bundle size conversation

The library takes bundle size seriously enough to publish both a bundlephobia badge and a size-limit step in its build.

The README says it gets tree shaking right by default, and also provides extra deep imports for each utility, so that embedding these utilities into your own library does not increase the bundle size reports of that library. That is a specific and somewhat rare courtesy. If you write a component library that uses Pacer internally, the deep import entry points are what let your consumers pull in only the debounce they use.

The build scripts show the same attention. The build command runs the affected targets through Nx and then size-limit, and there is a build:core target that builds only the core packages, packages/pacer and packages/pacer-lite, and then runs size-limit. The existence of a pacer-lite package suggests a smaller build for consumers who do not need everything.

The workspace is pnpm-based with a pnpm-workspace.yaml and pnpm-lock.yaml, and the package manager is pinned to [email protected]. Node is required at ^24.21.0 or 26.0.0 or newer, and pnpm at 12.6.0 or newer.

Type safety is called out as a feature: full type safety with TypeScript that makes sure your functions are always called with the correct arguments, and generics for flexible and reusable utilities.

The repository is a monorepo with Nx, examples for every adapter, and generated docs

The layout shows how the project is organised to keep a dozen packages in step.

There is a packages/ directory, an examples/ directory with one example per framework including a vanilla one, a docs/ directory, and a scripts/ directory. Nx drives the build, and nx.json plus a .nx/ directory are at the root. The root package.json uses a .changeset/ directory for versioning, with a changeset script, a changeset:version script and a changeset:publish script, which is the standard release flow for a multi-package project where each adapter is published independently.

Documentation is generated rather than written twice. There is a generate-docs script that runs scripts/generate-docs.ts and then copies the readme, and a typedoc.json for the API reference. The test suite is broad: a test:ci script chains per-project tests with nx run-many across targets named test:e2e:types, test:e2e:lint, test:eslint, test:sherif, test:knip, test:docs, test:lib, test:types and build, followed by size-limit. There is a knip.json for unused dependency and export detection, a sherif test for package.json consistency across the workspace, a verify-links script for the docs, and a playwright.config.ts for browser tests.

That is a lot of machinery, and the reason is the same as the ten adapters: consistency across packages is the hard part, and the tooling is what makes it cheap.

The last push to the repository was on 2026-10-14 and the repository is not archived. The licence is MIT.

Editorial conclusion

Adopt TanStack Pacer if you are writing a TypeScript front end and want debouncing, throttling, rate limiting, queuing and batching that behave consistently and can be shared between a sync handler and an async one. Do not adopt it as a server-side rate limiter today, because the README states it is currently mostly a client-side only library even though it is being designed to work on the server as well. Before you upgrade, check whether the Svelte and Vue adapters still need a contributor, since the README flags both, and decide early whether you want the full package or the deep imports, because the bundle size story depends on which entry points you use.

Frequently asked questions

What does TanStack Pacer actually provide?

Utilities for debouncing, throttling, rate limiting, queuing and batching, in both synchronous and asynchronous versions, with promise support, error handling, and retry and abort support for the async variants. Rate limiting offers fixed and sliding window choices, and queues offer FIFO, LIFO and priority ordering.

Which frameworks have a TanStack Pacer adapter?

React, Preact, Solid, Angular, Vue, Svelte, Lit, Alpine, Ember and Octane. The README notes that the Svelte and Vue adapters still need a contributor.

Can TanStack Pacer be used on a server?

Not yet as a supported use. The README states that Pacer is currently mostly a client-side only library, though it is being designed to be able to potentially be used on the server side as well.

How do I install TanStack Pacer and what are the requirements?

The npm package is @tanstack/pacer, and the repository workspace uses [email protected] with node ^24.21.0 or 26.0.0 or newer, and pnpm 12.6.0 or newer. The project also provides deep imports per utility for libraries that embed it.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. TanStack/pacer on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tanstack-pacer.svg)](https://hysenlabs.com/projects/tanstack-pacer)

Was this useful?