TanStack Pacer

TanStack Pacer is a library from the TanStack team where they share the high quality utilities for controlling function execution timings in the applications.
TanStack Pacer is currently a client-side only library but it is designed to be used in server-side as well.
Features of TanStack Pacer
Debouncing - Delay execution until after a period of inactivity for when you only care about the last execution in a sequence.
Throttling - Smoothly limit the rate at which a function can fire
Rate Limiting - Limit the rate at which a function can fire over a period of time
Queuing - Queue functions to be executed in a specific order, Choose from FIFO, LIFO, and Priority queue implementations
Batching - Chunk up multiple operations into larger batches to reduce total back-and-forth operations
Async or Sync Variations - Choose between synchronous and asynchronous versions of each utility
State Management - Uses TanStack Store under the hood for state management with fine-grained reactivity
Convenient Hooks - Reduce boilerplate code with pre-built hooks like useDebouncedCallback, useThrottledValue, and useQueuedState, and more.
Tree-shaking - Get tree-shaking right for your applications by default
Type safety - Full type safety with TypeScript that makes sure that your functions will always be called with the correct arguments
Installation
You can install TanStack Pacer.
To install it in React you can use the below command
npm install @tanstack/react-pacer
To use the devtools for debugging and monitoring, install both the framework devtools and the Pacer devtools packages
npm install @tanstack/react-devtools @tanstack/react-pacer-devtools
Debouncing
Debouncing is a technique that delays the execution of a function until a specified period of inactivity time occured
"use client"
import { useState, useEffect } from "react"
import { useDebouncedValue } from "@tanstack/react-pacer"
export default function ProductSearch() {
const [input, setInput] = useState("")
const [results, setResults] = useState<string[]>([])
// Debounce input by 400ms
const [debouncedInput] = useDebouncedValue(input, { wait: 400 })
useEffect(() => {
if (!debouncedInput) {
setResults([])
return
}
const fetchData = async () => {
const response = await fetch(`/api/products?query=${debouncedInput}`)
const json = await response.json()
setResults(json.items)
}
fetchData()
}, [debouncedInput])
return (
<div className="space-y-2">
<input
className="border p-2 w-full"
placeholder="Search products..."
value={input}
onChange={(e) => setInput(e.target.value)}
/>
{/* Render suggestions */}
{results.length > 0 && (
<ul className="border rounded p-2 bg-white">
{results.map(item => (
<li key={item} className="py-1 border-b last:border-none">
{item}
</li>
))}
</ul>
)}
</div>
)
}
The above example updates the url after 400ms
Throttling
Throttling ensures function executions are evenly spaced over time. Unlike rate limiting which allows bursts of executions up to a limit, or debouncing which waits for activity to stop, throttling creates a smoother execution pattern by enforcing consistent delays between calls.
"use client"
import { throttle } from "@tanstack/react-pacer"
export default function SafeButton() {
const handleClick = throttle(() => {
alert("Action performed")
}, { wait: 1000 }) // only allowed once per second
return (
<button
onClick={handleClick}
className="px-4 py-2 bg-blue-600 text-white rounded"
>
Click Me Fast — I’m Throttled
</button>
)
}
Throttled Button (prevents spam clicks) useful for API calls, forms submission or payment prompts
Rate Limiting
Rate Limiting is a technique that limits the rate at which a function can execute over a specific time window. It is particularly useful for scenarios where you want to prevent a function from being called too frequently, such as when handling API requests or other external service calls.
import { rateLimit } from "@tanstack/react-pacer"
const safeLogin = rateLimit(
async (email: string, password: string) => {
console.log("Login attempted")
// Backend login API here
return { success: true }
},
{ limit: 3, interval: 30_000 } // allow 3 attempts every 30s
)
// Usage
safeLogin("user@mail.com", "pass123").then(console.log)
safeLogin("user@mail.com", "pass123").then(console.log)
// 4th call within 30s will be rate-limited
Queuing
Queuing ensures that every operation is eventually processed, even if they come in faster than they can be handled. Unlike the other execution control techniques that drop excess operations, queuing buffers operations in an ordered list and processes them according to specific rules.
import { queue } from "@tanstack/react-pacer"
const writeToDB = queue(
async (record: any) => {
console.log("Writing record:", record.id)
await fetch("/api/db/save", {
method: "POST",
body: JSON.stringify(record),
headers: { "Content-Type": "application/json" }
})
},
{ concurrency: 3 } // controlled parallelism
)
// Usage
records.forEach(r => writeToDB(r))
Queue Database Writes to Avoid Locking. Batch DB operations without hammering the server.
Batching
Batching collects items over time or until a certain size is reached, then processes them all at once. This is ideal for scenarios where processing items in bulk is more efficient than handling them one by one.
import { batch } from "@tanstack/react-pacer"
const queueMessages = batch(async (msgs: string[]) => {
await fetch("/api/messages/bulk", {
method: "POST",
body: JSON.stringify(msgs),
headers: { "Content-Type": "application/json" }
})
}, { maxSize: 20, wait: 1500 }) // send when 20 msgs or 1.5s idle
// Usage
queueMessages("Hello")
queueMessages("How are you?")
queueMessages("What's new?")
Batch messages before sending to server. Reduces network calls by grouping multiple messages.
Final Notes
Using the Tanstack Pacer in your application helps precise control over function execution, leading to smoother UX, reduced API costs, and improved performance. Whether you’re handling user input, managing API calls, or orchestrating complex async workflows, these utilities are your one-stop shop for execution timing




