Tanstack Pacer

by tanstack-skills6f5521ecbdfbNo license35 starsListed Oct 8, 2026Updated Oct 8, 2026Repository updated 8 months ago

Framework-agnostic debouncing, throttling, rate limiting, queuing, and batching utilities.

Instructions onlySoftware Development
AI-generated overview

Reference guide for TanStack Pacer's debouncing, throttling, rate limiting, queuing and batching utilities.

What it does
This skill is a documentation-style reference for the TanStack Pacer library, covering debouncing, throttling, rate limiting, queuing and batching of function execution. It shows class APIs, factory functions and React hooks, with code examples for options such as wait, leading, trailing, maxWait, limit, window, concurrency and batch size. It also includes guidance on choosing a utility, leading versus trailing edge behavior, best practices and common pitfalls.
When to use it
Use it when you need to look up how to control the timing or frequency of function calls in JavaScript or TypeScript, such as debouncing a search input, throttling scroll handlers, limiting API calls or batching analytics events. It is also useful when deciding which Pacer utility fits a given scenario.
Requirements
Requires the @tanstack/pacer package, and @tanstack/react-pacer for the React hooks. No scripts are included; the skill is instructions and code examples only.

Overview

TanStack Pacer provides a unified, type-safe toolkit for controlling function execution timing. It offers class-based APIs, factory functions, and React hooks for debouncing, throttling, rate limiting, queuing, and batching.

Core: @tanstack/pacer React: @tanstack/react-pacer Status: Beta

Installation

bash
npm install @tanstack/pacernpm install @tanstack/react-pacer  # React hooks

Debouncing

Delays execution until after a period of inactivity.

Class API

typescript
import { Debouncer } from '@tanstack/pacer'
const debouncer = new Debouncer(  (query: string) => fetchSearchResults(query),  {    wait: 300,            // ms of inactivity before execution    leading: false,       // Execute on leading edge (default: false)    trailing: true,       // Execute on trailing edge (default: true)    maxWait: 1000,        // Force execution after 1s of continuous calls    enabled: true,    onExecute: (result) => console.log(result),  })
debouncer.maybeExecute('search term')debouncer.cancel()debouncer.getExecutionCount()debouncer.setOptions({ wait: 500 }) // Dynamic reconfiguration

Factory Function

typescript
import { debounce } from '@tanstack/pacer'
const debouncedSearch = debounce(  (query: string) => fetchResults(query),  { wait: 300 })
debouncedSearch('term')debouncedSearch.cancel()

React Hooks

typescript
import {  useDebouncer,  useDebouncedCallback,  useDebouncedState,  useDebouncedValue,} from '@tanstack/react-pacer'
// Full debouncer instanceconst debouncer = useDebouncer(fn, { wait: 300 })
// Simple debounced functionconst debouncedFn = useDebouncedCallback(fn, { wait: 300 })
// Debounced state managementconst [debouncedValue, setValue] = useDebouncedState(initialValue, { wait: 300 })
// Debounced reactive valueconst debouncedValue = useDebouncedValue(reactiveValue, { wait: 300 })

Throttling

Limits execution to at most once per interval.

Class API

typescript
import { Throttler } from '@tanstack/pacer'
const throttler = new Throttler(  (position: { x: number; y: number }) => updatePosition(position),  {    wait: 100,            // Minimum interval between executions    leading: true,        // Execute immediately on first call (default: true)    trailing: true,       // Execute after interval with last args (default: true)    enabled: true,    onExecute: (result) => console.log(result),  })
throttler.maybeExecute({ x: 100, y: 200 })throttler.cancel()

React Hooks

typescript
import {  useThrottler,  useThrottledCallback,  useThrottledState,  useThrottledValue,} from '@tanstack/react-pacer'
const throttledFn = useThrottledCallback(handleScroll, { wait: 100 })const [throttledPos, setPos] = useThrottledState({ x: 0, y: 0 }, { wait: 100 })

Rate Limiting

Controls execution with a maximum count within a time window.

Class API

typescript
import { RateLimiter } from '@tanstack/pacer'
const limiter = new RateLimiter(  async (endpoint: string) => fetch(endpoint).then(r => r.json()),  {    limit: 10,            // Max executions per window    window: 60000,        // Time window in ms (60s)    enabled: true,    onExecute: (result) => console.log(result),    onReject: (...args) => console.warn('Rate limited:', args),  })
limiter.maybeExecute('/api/data')  // Rejected if limit exceededlimiter.getExecutionCount()limiter.getRejectionCount()

React Hooks

typescript
import {  useRateLimiter,  useRateLimitedCallback,  useRateLimitedState,  useRateLimitedValue,} from '@tanstack/react-pacer'
const rateLimitedFn = useRateLimitedCallback(apiCall, { limit: 5, window: 1000 })

Queuing

Sequential execution with configurable concurrency.

typescript
import { Queue } from '@tanstack/pacer'
const queue = new Queue({  concurrency: 1,         // Max concurrent tasks  started: true,          // Start processing immediately})
queue.add(() => uploadFile(file1))queue.add(() => uploadFile(file2))
queue.start()queue.pause()queue.clear()queue.getSize()           // Pending countqueue.getPending()        // Currently executing count

Batching

Groups calls for combined processing.

typescript
import { Batcher } from '@tanstack/pacer'
const batcher = new Batcher(  (items: LogEntry[]) => sendBatchToServer(items),  {    maxSize: 50,          // Auto-flush at 50 items    wait: 1000,           // Auto-flush after 1s  })
batcher.add(logEntry)    // Accumulatesbatcher.flush()          // Manual flushbatcher.getSize()        // Current batch sizebatcher.clear()          // Discard batch

Async Variants

typescript
import { AsyncDebouncer, asyncDebounce, AsyncThrottler, asyncThrottle } from '@tanstack/pacer'
const asyncDebouncer = new AsyncDebouncer(  async (query: string) => {    const response = await fetch(`/api/search?q=${query}`)    return response.json()  },  { wait: 300 })
// React async hooksimport { useAsyncDebouncer, useAsyncThrottler } from '@tanstack/react-pacer'

Choosing the Right Utility

ScenarioUtilityWhy
Search inputDebouncerWait for user to stop typing
Scroll eventsThrottlerPeriodic updates during activity
API protectionRateLimiterHard limit on call frequency
File uploadsQueueSequential processing
Analytics eventsBatcherGroup for efficiency
Network requestsAsyncDebouncerHandle abort/retry

Leading vs Trailing Edge

  • Leading (leading: true): Execute immediately, suppress until wait expires. Good for button clicks.
  • Trailing (trailing: true): Execute after activity stops. Good for search inputs.
  • Both: Execute immediately AND after final wait. Good for scroll throttling.

Best Practices

  1. Use maxWait with debouncing to guarantee execution during continuous activity
  2. Use async variants for network requests (handle abort/cancellation)
  3. React hooks handle cleanup automatically - no manual teardown needed
  4. Use setOptions for dynamic reconfiguration (e.g., reducing wait for power users)
  5. Compose utilities for complex scenarios (rate-limited queue)
  6. Use onReject on RateLimiter to inform users when they're rate limited

Common Pitfalls

  • Using debounce when you need throttle (debounce waits for inactivity, throttle guarantees periodic execution)
  • Not using maxWait with debounce for long-running continuous events
  • Creating new instances on every render (use hooks or module-level)
  • Forgetting cleanup in non-React environments (call cancel())

Source and attribution

Source:tanstack-skills/tanstack-skillsinplugins/tanstack-pacer/skills/tanstack-pacerat commit6f5521e

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal