A Web Worker runs JavaScript in a separate execution context, letting CPU-heavy computation run without occupying the page’s main UI thread. In React, the component sends plain data to the worker and uses the returned message to update React state; the worker cannot directly update the page DOM. In Next.js, that application-created worker is different from next/script’s experimental strategy="worker", which is intended for eligible third-party scripts.
What a Web Worker does—and what it cannot do
A dedicated Web Worker is a browser-managed JavaScript context separate from the page’s main thread. It is suited to work such as large calculations or data processing that would otherwise keep the main thread busy and make interactions or rendering feel unresponsive. It is not a guarantee of a speedup: starting a worker and exchanging messages have costs, so measure the actual workload.
The page and worker communicate with postMessage() and message events. Messages are generally structured-cloned, which means large inputs and outputs can take time and memory to copy. For supported data types, transferable objects can move ownership rather than copy the underlying data. See MDN’s Web Workers guide for the browser model and communication details.
Can a Web Worker update the DOM?
No. A dedicated worker does not have direct access to the page’s window or DOM, so it cannot call React setters or manipulate elements. It sends a result to the page; the main thread handles that message and updates React state, after which React renders the UI. Pass serializable input and output, not component closures, functions, or DOM nodes.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problems#1 Best Overall
How do I use Web Workers in React?
Create the worker from client-side code, send it input, handle its messages on the page, and terminate it when its owner is finished. The example below counts prime numbers up to a chosen limit. It demonstrates the message flow; it is not a performance benchmark.
1. Create the worker file
Place prime-worker.js alongside the component that references it. Worker code uses its own global context, so use self and worker APIs rather than page-only globals.
self.onmessage = (event) => {
const limit = event.data.limit;
let count = 0;
for (let candidate = 2; candidate <= limit; candidate++) {
let isPrime = true;
for (let divisor = 2; divisor * divisor <= candidate; divisor++) {
if (candidate % divisor === 0) {
isPrime = false;
break;
}
}
if (isPrime) count++;
}
self.postMessage({ limit, count });
};
2. Create and own the worker in the React component
This component creates the worker after mounting, so worker construction does not run during server rendering. The URL form shown is the bundler-aware pattern MDN describes for webpack, Vite, and Parcel; confirm that the installed bundler and its configuration support it, and check the emitted worker path and syntax in your project.
import { useEffect, useRef, useState } from 'react';
export function PrimeCounter() {
const workerRef = useRef(null);
const [limit, setLimit] = useState(100000);
const [result, setResult] = useState(null);
const [error, setError] = useState('');
const [pending, setPending] = useState(false);
useEffect(() => {
const worker = new Worker(
new URL('./prime-worker.js', import.meta.url)
);
workerRef.current = worker;
worker.onmessage = (event) => {
setResult(event.data);
setError('');
setPending(false);
};
worker.onerror = (event) => {
setError(event.message || 'The worker failed.');
setPending(false);
};
return () => {
worker.terminate();
workerRef.current = null;
};
}, []);
function calculate() {
const worker = workerRef.current;
if (!worker || pending) return;
setResult(null);
setError('');
setPending(true);
worker.postMessage({ limit: Number(limit) });
}
return (
<section>
<label>
Count primes up to
<input
type="number"
min="2"
value={limit}
onChange={(event) => setLimit(event.target.value)}
/>
</label>
<button onClick={calculate} disabled={pending}>
{pending ? 'Calculating…' : 'Calculate'}
</button>
{error && <p role="alert">{error}</p>}
{result && (
<p>
There are {result.count} primes up to {result.limit}.
</p>
)}
</section>
);
}
The worker receives a plain object and returns another plain object. The message handler runs on the page side, where calling state setters is appropriate. This example disables another request while one is pending to avoid out-of-order results. The cleanup calls terminate(), which stops the worker when the component unmounts; if a worker is shared more broadly, give it an application-level owner and cleanup policy instead.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Rank #3
3. Check compatibility in the actual project
Worker URL handling and supported module syntax depend on the project’s bundler and versions. MDN documents new Worker(new URL('./worker.js', import.meta.url)) as the recommended bundler-aware construction pattern for webpack, Vite, and Parcel, but that does not establish identical setup instructions for every React or Next.js configuration. Consult the project’s installed-version documentation and verify the build output rather than assuming a file convention will work unchanged.
How do I add a Web Worker in Next.js?
For custom computation, create or use the browser worker in client-side code. In the App Router, that means a Client Component (marked with 'use client') when the component needs client behavior. Keep worker creation in an effect or another browser-only path, not in server-rendered code. The same message-and-state pattern applies, but the precise worker bundling setup remains dependent on the Next.js and bundler versions in use.
Rank #4
Do not confuse this with next/script’s strategy="worker". That option is a separate mechanism for offloading scripts, including certain third-party scripts through Partytown; it is not a general API for running your own calculation worker.
Does Next.js support Web Workers in the App Router?
Application-created browser workers and the Script component strategy have different answers. A custom worker can be created by client-side browser code, subject to the project’s bundler support. The current Next.js Script Component reference says: “The worker strategy is not yet stable and does not yet work with the App Router. Use with caution.” The Next.js scripts guide says the strategy uses Partytown, requires the experimental.nextScriptWorkers: true flag, and currently can only be used in pages/. It also notes that development guides installation of @qwik.dev/partytown. Treat these experimental labels, setup details, and compatibility statements as version-sensitive; check the docs for the Next.js version installed in your project.
Free tools Windows power users keep installed
One-click scans. No signup required.
Best Value
Which approach should you choose?
| Approach | Use it when | Trade-offs and checks |
|---|---|---|
| Main-thread JavaScript | The work is small, quick, or needs synchronous access to page state or the DOM. | No worker messaging or lifecycle to manage, but a long-running computation can block UI work. |
| Application-created dedicated worker | Your own CPU-heavy task can operate on message data without direct DOM access. | Account for worker startup, structured-clone costs, errors, cancellation, and cleanup. Use transferables when the data type and API support them and copying is a concern. |
Next.js Script strategy="worker" |
You are considering offloading an eligible third-party script through the documented Partytown integration. | Experimental, limited to the Pages Router according to current docs, and third-party script compatibility is not guaranteed. It is not a replacement for a custom compute worker. |
Before moving a task, ask whether its CPU cost is large enough to outweigh message overhead; whether it requires synchronous state or direct DOM access; how large the messages are; and who will report errors, stop work, and dispose of the worker. For third-party scripts, also check the router and the script’s compatibility with Partytown. Benchmark with realistic inputs and include serialization costs: the official guidance describes the intended way to keep the UI thread available, not a guaranteed gain for a particular application.
Quick Recap
Common integration mistakes
- Creating the worker during server rendering: worker construction is a browser operation; create it in client-side code, such as an effect.
- Expecting the worker to touch React or the DOM: return data in a message and make UI changes in the page’s message handler.
- Sending large payloads without accounting for copies: structured cloning has a cost; consider supported transferable objects for suitable data.
- Leaving workers running after their owner goes away: define ownership and cleanup, and call
terminate()for a dedicated worker when it is no longer needed. - Assuming every Next.js “worker” feature is interchangeable: a custom browser worker handles application computation; the Script strategy is a distinct, experimental third-party-script integration.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




