October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk6 min

How to Use Web Workers in React and Next.js

Web Workers can move CPU-heavy JavaScript off the page’s main thread. Learn the React message pattern, bundler caveats, and how it differs from Next.js’s experimental third-party script strategy.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.