A simple web worker demo with TypeScript and NextJS


2 min read

πŸ—» Prerequisites

In this demo, we are using (on 2023-02-06):

  • NextJS: 13.1.5

  • TypeScript template

  • Bundler: Turbopack or webpack

Let's create a simple web worker demo!

🌻 Create a simple web worker demo

Because we can use Worker on any browser, so we don't need to check if (typeof Worker !== 'undefined'):

if (typeof Worker !== 'undefined') {
  // Create a new Worker.
} else {
  // Web workers are not supported in this environment.

We need new URL() , the project can't detect the real path of ../workers/plus when source code is built with bundler (e.g. webpack).

Import webworker script without new URL() :

const plusWorker = new Worker('../workers/plus');

Import webworker script with new URL() :

const plusWorker = new Worker(new URL('../workers/plus', import.meta.url));

I develop with the next dev --turbo script. This is turbopack mode (A webpack alternative). But unfortunately, the following error occurred:

Refused to execute script from 'http://localhost:3000/_next/static/assets/d6f517fec7dcd080.ts' because its MIME type ('video/vnd.dlna.mpeg-tts') is not executable.

Then, I back to webpack with the next dev script, everything works fine! Maybe vercel turbo is a very new tool and there are still many issues with it at this time.

Here is the complete code. src/pages/index.tsx file's content:

import Head from 'next/head';
import styles from '@/styles/Home.module.css';
import { useEffect } from 'react';

export default function Home() {
  useEffect(() => {
    const plusWorker = new Worker(new URL('../workers/plus', import.meta.url));

    plusWorker.onmessage = (event) => {
      console.log('🍏 Message received from worker: ', event.data);

    plusWorker.onerror = (event) => {
      if (event instanceof Event) {
        console.log('🍎 Error message received from worker: ', event);
        return event;

      console.log('🍎 Unexpected error: ', event);
      throw event;

    plusWorker.postMessage([1, 2]);

    return () => {
  }, []);

  return (
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="icon" href="/favicon.ico" />
      <main className={styles.main}>
        <div className={styles.description}>
          <p>Get started by editing</p>

src/workers/plus.ts file's content:

import { TWorkerMess } from '@/models';

const onmessage = (event: MessageEvent<TWorkerMess>) => {
  console.log('🐝 Worker: Message received from main script');
  const data = event.data;
  const result = data[0] + data[1];

  const workerResult = 'Result: ' + result;
  console.log('🐝 Worker: Posting message back to main script');

addEventListener('message', onmessage);

src/models/worker.ts file's content:

export type TWorkerMess = number[];

When run next dev or pnpm dev output on console log is below:

🐝 Worker: Message received from main script

🐝 Worker: Posting message back to main script

🍏 Message received from worker: Result: 3

We did it!

πŸƒ References