Keep React Responsive During Heavy Computation with Web Workers and useRef
A page that freezes for seconds during a calculation feels broken to users, and the fix is not more useMemo — it's moving work off the main thread. This pattern gives React developers a clean, reusable abstraction for Web Workers that avoids the common pitfalls of duplicate instances and memory leaks.
Long-running JavaScript blocks the browser's rendering thread, freezing the UI until the computation finishes. Web Workers run scripts on a separate thread, but integrating them into React's render cycle is tricky: creating a Worker inside a component function spawns a new instance on every re-render, leaking memory and breaking state.
The fix is a custom hook that stores the Worker instance in a useRef, which persists across renders without triggering updates. The hook initializes the Worker once on mount, listens for result messages, and terminates it on unmount. A useState slice handles the loading, result, and error states that actually need to drive the UI.
A full TypeScript example walks through a Fibonacci calculator that would normally lock the page. The pattern extends to image processing, large file parsing, on-device AI inference, and regex highlighting across thousands of records — any task where the cost of structured-clone data transfer is outweighed by keeping the main thread free.
The pattern treats the Worker as an imperative, long-lived object that sits outside React's declarative model — useRef is the escape hatch that makes this possible without fighting the framework.
Many React performance discussions stop at memoization and virtualization; this approach addresses a harder class of problem where the computation itself, not the rendering, is the bottleneck.
The structured-clone overhead means Workers are not a free lunch — the break-even point depends on whether the computation cost dominates the serialization cost, which matters for real-time or high-frequency tasks.