Using Web Workers in a TypeScript React project based on create-react-app.
This project is an example React application that uses Web Workers. You can clone it and play around with it (see Commands).
The following sub-chapters explain how to setup Web Worker support in a create-react-app project, and how to use Web Workers in your app -
both vanilla and using Comlink.
First of all, we need a few new dependencies. In particular:
- We need react-app-rewired to hook into the Webpack configuration that react-scriptsuses under the hood (without having to eject the config)
- We use the Webpack worker-loader to integrate Web Workers into our build process
Add both dependencies to your package.json file and install them. For example:
  {
    "devDependencies": {
+     "react-app-rewired": "2.1.x",
+     "worker-loader": "3.0.x"
    }
  }First, replace all mentions of react-scripts within the scripts area of your package.json file by react-app-rewired. This enables us
to tap into the build process in the next step. For example:
  {
    "scripts": {
-     "start": "react-scripts start",
+     "start": "react-app-rewired start",
-     "build": "react-scripts build",
+     "build": "react-app-rewired build",
-     "test": "react-scripts test",
+     "test": "react-app-rewired test",
    }
  }Then, create a file named react-app-rewired.config.js (or whatever name you prefer) within the root folder of your project. This file will
be used by react-app-rewired when the build process runs, and allows us to customize the underlying Webpack configuration before the build
runs. Fill it with the following content:
/**
 * React App Rewired Config
 */
module.exports = {
  // Update webpack config to use custom loader for worker files
  webpack: (config) => {
    // Note: It's important that the "worker-loader" gets defined BEFORE the TypeScript loader!
    config.module.rules.unshift({
      test: /\.worker\.ts$/,
      use: {
        loader: 'worker-loader',
        options: {
          // Use directory structure & typical names of chunks produces by "react-scripts"
          filename: 'static/js/[name].[contenthash:8].js',
        },
      },
    });
    return config;
  },
};Finally, reference the react-app-rewired.config.js file in your package.json file by adding the following line:
  {
+   "config-overrides-path": "react-app-rewired.config.js",
  }Now you can start using Web Workers! Two things are important here: Files that contain a Web Worker must end with *.worker.ts, and they
must start with the following two lines of code in order to work nicely together with TypeScript:
declare const self: DedicatedWorkerGlobalScope;
export default {} as typeof Worker & { new (): Worker };
// Your code ...In your application, you can import your Web Workers like a normal module, and instantiate them as a class. For example:
import MyWorker from './MyWorker.worker';
const myWorkerInstance: Worker = new MyWorker();Implementation pointers:
Bonus: Using Comlink
Using Web Workers as is comes with the additional overhead of messaging between the main thread and the worker thread. Comlink is a library by Googlers that simplifies the usage of Web Workers by turning the message-based communication into a "remote function execution"-like system.
Within your React app, you can use Comlink just like you would expect. For example, expose your API within your worker:
import { expose } from 'comlink';
export default {} as typeof Worker & { new (): Worker };
// Define API
export const api = {
  createMessage: (name: string): string => {
    return `Hello ${name}!`;
  },
};
// Expose API
expose(api);Then, from within you main thread, wrap the instantiated worker and use the worker API (asynchronously):
import { wrap } from 'comlink';
import MyComlinkWorker, { api } from './MyComlinkWorker.worker';
// Instantiate worker
const myComlinkWorkerInstance: Worker = new MyComlinkWorker();
const myComlinkWorkerApi = wrap<typeof api>(myComlinkWorkerInstance);
// Call function in worker
myComlinkWorkerApi.createMessage('John Doe').then((message: string) => {
  console.log(message);
});Implementation pointers:
Soooo, Jest does not support Web Workers (see jest/#3449). Plus, Jest does not use our customized Webpack configuration anyways. Thus - using one of the many ways you can mock stuff in Jest - mock away Web Workers when testing code that instantes them / works with them.
The following commands are available:
| Command | Description | CI | 
|---|---|---|
| npm start | Creates a development build, running in watch mode | |
| npm run build | Creates a production build | ✔️ | 
| npm run start:build | Runs the production build | |
| npm run test | Executes all unit tests | ✔️ | 
| npm run test:watch | Executes all unit tests, running in watch mode |