---
name: tanstack-pacer
description: |
TanStack Pacer best practices for execution control in React — debouncing, throttling, rate limiting, queuing, and batching.
Use when implementing search inputs, scroll handlers, API rate limits, task queues, bulk operations,
or any scenario requiring controlled execution timing with reactive state.
metadata:
tags: tanstack-pacer, debounce, throttle, rate-limit, queue, batch, react, typescript, execution-control
---
# TanStack Pacer
**Version**: @tanstack/react-pacer@latest
**Requires**: React 16.8+, TypeScript recommended
## Quick Setup
```bash
npm install @tanstack/react-pacer
```
```tsx
import { useDebouncedCallback } from '@tanstack/react-pacer'
function SearchInput() {
const debouncedSearch = useDebouncedCallback(
(query: string) => fetchResults(query),
{ wait: 300 },
)
return debouncedSearch(e.target.value)} />
}
```
### 4 Hook Variants Per Utility
Each utility (Debouncer, Throttler, RateLimiter, Queuer, Batcher) provides 4 React hooks:
| Hook | Returns | Use Case |
|------|---------|----------|
| `use[Utility]` | Instance | Full control, custom state subscriptions |
| `use[Utility]Callback` | Wrapped function | Simple event handler wrapping |
| `use[Utility]State` | `[value, setValue, instance]` | Debounced/throttled React state |
| `use[Utility]Value` | `[derivedValue, instance]` | Read-only derived value from props/state |
### PacerProvider (Optional)
```tsx
import { PacerProvider } from '@tanstack/react-pacer'
```
### Devtools
```bash
npm install -D @tanstack/react-devtools @tanstack/react-pacer-devtools
```
```tsx
import { TanStackDevtools } from '@tanstack/react-devtools'
import { pacerDevtoolsPlugin } from '@tanstack/react-pacer-devtools'
```
Utilities must have a `key` option to appear in devtools.
## Rule Categories
| Priority | Category | Rule File | Impact |
|----------|----------|-----------|--------|
| CRITICAL | Hook Selection | `rules/hook-selection.md` | Correct hook choice per use case |
| CRITICAL | Debouncing | `rules/deb-debouncing.md` | Prevents wasted API calls and flickering UI |
| HIGH | Throttling | `rules/thr-throttling.md` | Smooth, evenly-spaced execution |
| HIGH | State & Reactivity | `rules/state-reactivity.md` | Prevents unnecessary re-renders |
| HIGH | Async Patterns | `rules/async-patterns.md` | Correct async execution with retry, abort, error handling |
| MEDIUM | Rate Limiting | `rules/rl-rate-limiting.md` | Enforces execution budgets |
| MEDIUM | Queuing | `rules/que-queuing.md` | Lossless ordered/priority task processing |
| MEDIUM | Batching | `rules/bat-batching.md` | Groups operations for bulk processing |
| LOW | Configuration | `rules/config-options.md` | Dynamic options, providers, shared config |
| LOW | Devtools | `rules/devtools.md` | Debugging and monitoring utilities |
## Critical Rules
### Always Do
- **Use hooks over function wrappers** — `useDebouncedCallback` not `debounce()` for proper React lifecycle
- **Choose the right hook variant** — `useCallback` for event handlers, `useState` for controlled inputs, `useValue` for derived values
- **Opt-in to state subscriptions** — pass selector as 3rd arg: `useDebouncer(fn, opts, (s) => ({ isPending: s.isPending }))`
- **Use async variants for API calls** — `useAsyncDebouncedCallback` gives error handling, retry, abort
- **Pass `AbortSignal` to fetch** — `getAbortSignal()` enables cancellation of in-flight requests
- **Use `key` option for devtools** — only keyed utilities appear in devtools panel
### Never Do
- **Subscribe to all state** — omit selector or use instance directly to avoid re-renders on every state change
- **Use debouncing when you need guaranteed execution** — use throttling or queuing instead
- **Use rate limiting for evenly-spaced calls** — rate limiting is bursty; use throttling for smooth spacing
- **Use `debounce()` function in React** — no lifecycle cleanup; use `useDebouncedCallback` hook instead
- **Expect `maxWait` on Debouncer** — Pacer has no `maxWait`; use Throttler for guaranteed periodic execution
## Key Patterns
```tsx
// Debounced search input with state
import { useDebouncedState } from '@tanstack/react-pacer'
function Search() {
const [query, setQuery, debouncer] = useDebouncedState('', { wait: 300 })
// query updates after 300ms pause; setQuery is immediate
return setQuery(e.target.value)} />
}
// Async debounced API call with abort
import { useAsyncDebouncedCallback } from '@tanstack/react-pacer'
function AsyncSearch() {
const search = useAsyncDebouncedCallback(
async (query: string) => {
const signal = search.getAbortSignal()
const res = await fetch(`/api/search?q=${query}`, { signal })
return res.json()
},
{ wait: 300, onSuccess: (data) => setResults(data) },
)
return search(e.target.value)} />
}
// Throttled scroll handler
import { useThrottledCallback } from '@tanstack/react-pacer'
function ScrollTracker() {
const onScroll = useThrottledCallback(
() => trackScrollPosition(window.scrollY),
{ wait: 100 },
)
useEffect(() => {
window.addEventListener('scroll', onScroll)
return () => window.removeEventListener('scroll', onScroll)
}, [onScroll])
}
// Derived debounced value from props
import { useDebouncedValue } from '@tanstack/react-pacer'
function FilteredList({ filter }: { filter: string }) {
const [debouncedFilter] = useDebouncedValue(filter, { wait: 300 })
return
}
// Async queue with concurrency
import { useAsyncQueuer } from '@tanstack/react-pacer'
function UploadQueue() {
const queuer = useAsyncQueuer(uploadFile, { concurrency: 3 })
return
}
```