npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@r01al/use-transition-worker

v0.1.2

Published

Run CPU-heavy work in a Web Worker and render the latest result with a React transition.

Readme

useTransitionWorker

useTransitionWorker combines Web Worker computation with React transition rendering. It returns request controls, result state, and pending indicators:

const { runTransition, isPending, cancel } =
	useTransitionWorker(workerHandler);

The Worker performs CPU-heavy computation away from the main thread. When the latest result arrives, the action passed to runTransition runs inside React's startTransition, so its state updates render at transition priority.

runTransition(input, action)
        ↓
Web Worker computes result
        ↓
React startTransition
        ↓
action(result)
        ↓
React renders the action's state update

Installation

npm install @r01al/use-transition-worker

React 18 or newer is required as a peer dependency.

Quick start

Define a self-contained Worker handler and apply its result to component state:

import { useState } from 'react';
import { useTransitionWorker } from '@r01al/use-transition-worker';

type SearchInput = {
	items: Array<{ id: number; title: string }>;
	query: string;
};

type SearchResult = Array<{ id: number; title: string }>;

function Search({ items }: { items: SearchResult }) {
	const [query, setQuery] = useState('');
	const [results, setResults] = useState<SearchResult>([]);

	const {
		runTransition,
		error,
		isPending,
		cancel,
	} = useTransitionWorker<
		SearchInput,
		SearchResult
	>(({ items: workerItems, query: workerQuery }) => {
		const normalizedQuery = workerQuery.toLowerCase();

		return workerItems.filter((item) =>
			item.title.toLowerCase().includes(normalizedQuery),
		);
	});

	function handleSearch() {
		void runTransition({ items, query }, setResults).catch((error) => {
			if (error instanceof Error && error.name === 'AbortError') {
				return;
			}

			console.error(error);
		});
	}

	return (
		<>
			<input value={query} onChange={(event) => setQuery(event.target.value)} />
			<button onClick={handleSearch} disabled={isPending}>
				{isPending ? 'Working…' : 'Search'}
			</button>
			<button onClick={cancel} disabled={!isPending}>
				Cancel
			</button>

			{error && <p>{error.message}</p>}
			{results.map((item) => (
				<p key={item.id}>{item.title}</p>
			))}
		</>
	);
}

The result action can perform more than one synchronous React state update:

void runTransition(input, (result) => {
	setResults(result);
	setSelectedId(null);
});

Awaiting a result

runTransition() returns Promise<Result>. This makes Worker failures observable and also lets non-rendering code inspect the computed value:

try {
	const result = await runTransition(input, setResults);
	console.log('Computed', result.length, 'items');
} catch (error) {
	console.error(error);
}

The promise resolves when the Worker response arrives. React may commit the state update from the result action slightly later.

Pending behavior

isPending is true while either:

  • The latest request is computing in the Worker.
  • React is rendering the result action's transition.

If the result action does not schedule a React state update, there is no transition render to remain pending after the Worker finishes.

Latest call wins

Calls can overlap:

void runTransition({ query: 'r', items }, setResults);
void runTransition({ query: 're', items }, setResults);
void runTransition({ query: 'react', items }, setResults);

Every returned promise resolves with its own Worker result, but only the latest request's result action runs. A stale response therefore cannot replace newer UI state.

Cancellation

cancel() terminates the current Worker, revokes its Blob URL, and rejects all unfinished request promises with an AbortError:

cancel();

Cancelled results cannot run their actions or update hook state. The next call to runTransition() lazily creates a fresh Worker.

Debugging

Debug logging is disabled by default:

const worker = useTransitionWorker(task, {
	debug: true,
});

Debug mode logs Worker lifecycle events and request IDs from the main thread and Worker. It never logs input or result values.

API

function useTransitionWorker<Input, Result>(
	handler: TransitionWorkerHandler<Input, Result>,
	options?: UseTransitionWorkerOptions,
): UseTransitionWorkerValue<Input, Result>;

Handler

type TransitionWorkerHandler<Input, Result> = (
	input: Input,
) => Result | Promise<Result>;

The handler is serialized into the Blob Worker. It must be self-contained and must not capture component variables or imported helpers.

Result action

The result action runs on the main thread inside React's startTransition. Unlike the Worker handler, it may use component closures and React state setters. State updates must be made synchronously within the action to inherit the transition priority.

Return value

type UseTransitionWorkerValue<Input, Result> = {
	runTransition: (
		input: Input,
		action: (result: Result) => void,
	) => Promise<Result>;
	result: Result | null;
	error: TransitionWorkerError | null;
	isComputing: boolean;
	isRendering: boolean;
	isPending: boolean;
	cancel: () => void;
};

result contains the latest successful non-stale result, while error contains the latest non-stale Worker error. Starting another request clears error; cancellation leaves the previous successful result available.

Options

type UseTransitionWorkerOptions = {
	debug?: boolean;
};

Worker function rules

A Worker has a different global environment from the React component. The handler cannot access:

  • React hooks or state setters.
  • Component closures.
  • Imported helpers.
  • The DOM, window, or document.

Pass everything required by the calculation through runTransition(input, action) or define helpers inside the handler. Inputs and results must support the browser's structured clone algorithm.

Browser and security requirements

The browser must support Web Workers, Blob URLs, Promises, and the structured clone algorithm. Because the Worker is created from a Blob URL, the application's Content Security Policy must allow:

worker-src blob:

Why both a Worker and useTransition?

React transitions do not move computation to another thread; they only change the priority of React state updates. This hook uses a Worker for parallel computation, then uses React's transition for rendering the returned result.