Skip to main content

useIdleCallbackWorker

Description and use case

useIdleCallbackWorker runs an expensive, synchronous task off the critical rendering path so it never freezes the UI thread or wrecks INP. It prefers an inline Web Worker (real parallelism, built from the task's source via a Blob URL) and transparently falls back to cooperative requestIdleCallback scheduling when Workers are unavailable — including during SSR.

Reach for it when a handler processes 10,000+ array items, runs heavy filtering, or does math-heavy work that would otherwise block interaction.

API signature

function useIdleCallbackWorker<TArgs extends unknown[], TResult>(
task: IdleWorkerTask<TArgs, TResult>,
options?: UseIdleCallbackWorkerOptions,
): UseIdleCallbackWorkerReturn<TArgs, TResult>;

Parameters

NameTypeRequiredDescription
taskIdleWorkerTask<TArgs, TResult>YesThe work to offload. On the worker path it is stringified, so it must be pure and not close over scope.
optionsUseIdleCallbackWorkerOptionsNoStrategy and timing controls (see below).

Options

NameTypeDefaultDescription
strategy'auto' | 'worker' | 'idle''auto'auto prefers a Worker and falls back to idle scheduling; worker forces a Worker; idle forces the main-thread path (closures allowed).
chunkBudgetMsnumber8Deadline for a single idle chunk before yielding.
timeoutMsnumber30000Rejects a pending task after this many ms.

Return value

FieldTypeDescription
execute(...args: TArgs) => Promise<TResult>Runs the task off the critical path and resolves with its result.
loadingbooleanWhether a task is currently in flight.
resultTResult | nullMost recent successful result, or null before first run.
errorError | nullMost recent error, or null when the last run succeeded.

Live interactive demo (StackBlitz)

GitHub Pages does not serve the isolation headers required for embedded StackBlitz WebContainers. Open the demo in StackBlitz to run it interactively.

Code example

import { useIdleCallbackWorker } from 'react-perf-hooks';

// Pure task — no closures, safe to run inside a Web Worker.
function filterLargeDataset(rows: number[], min: number): number[] {
return rows.filter((value) => value >= min);
}

export function SearchPanel({ rows }: { rows: number[] }) {
const { execute, loading, result, error } = useIdleCallbackWorker(filterLargeDataset);

const handleSearch = async () => {
const filtered = await execute(rows, 5000);
console.log(`matched ${filtered.length} rows`);
};

return (
<div>
<button type="button" onClick={handleSearch} disabled={loading}>
{loading ? 'Working…' : 'Filter'}
</button>
{error && <p role="alert">Failed: {error.message}</p>}
{result && <p>Matched {result.length} rows</p>}
</div>
);
}

Companion article