Web Workers to keep the main thread responsive
The main thread handles layout, paint, input, and your JavaScript. Long tasks (>50ms) hurt Interaction to Next Paint (INP). Web Workers run JS on a background thread with message-passing—no DOM access, but ideal for CPU-heavy work.
Good candidates
- Large JSON parsing from uploads.
- Image or audio decoding pipelines.
- Scientific transforms, CSV aggregation, diffing large lists.
- WASM modules compiled for worker contexts.
Message passing costs
Structured clone copies data by default. For big binary payloads, transfer ownership:
const buf = new ArrayBuffer(8);
worker.postMessage({ buf }, [buf]);
// buf is neutered in the sender
Ergonomics with Comlink
Comlink wraps postMessage with async proxies so worker APIs look like normal async functions.
Bundling workers
Vite and Webpack support ?worker imports or new Worker(new URL('./worker.ts', import.meta.url)) for correct chunking. Ensure workers are included in production build analysis.
Fallbacks
If Worker is unavailable (very old environments), run the same code on the main thread behind requestIdleCallback or split into chunks with setTimeout(0)—not as good, but preserves functionality.
Summary
Workers are underused in typical CRUD apps but essential when the product does real computation in the browser. Profile first, then move hot paths off the main thread with transferables and small messages.