TIL: Using Web Workers with Phoenix Liveview

TIL: Using Web Workers with Phoenix Liveview

In a LiveView project I'm working on, I had to add a web worker. I was a bit unsure of how to do this, and was worried I'd have to do some esbuild config manipulation, but it turned out to be quite simple.

For readers reading in the far future, these are the relevant versions with which I'm working in this post:

  {:phoenix_live_view, "~> 0.20.1"},
  {:esbuild, "~> 0.7", runtime: Mix.env() == :dev},

Add the worker file

Here's a simple worker.js

// assets/js/worker.js
self.onmessage = function(e) {
  // Worker logic here
  const result = e.data * 2; // Example: double the input
  self.postMessage(result);
};

Pass the worker file to esbuild

In config.exs, update the :esbuild config to

config :esbuild,
  version: "0.17.11",
  default: [
    args:
      ~w(js/app.ts js/worker.js --bundle --target=es2020 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),
    cd: Path.expand("../assets", __DIR__),
    env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
  ]

Use it from app.js or a hook

Depending on what your use case, you might want to load the worker immediately, or load it in the mounted step of a JS hook. For example, if you want to load the worker after the Liveview is loaded, you can do:

window.addEventListener("phx:page-loading-stop", async _info => {
    topbar.hide()

    // this is the path where the compiled JS file will be placed
    const worker = new Worker('/assets/worker.js');

    worker.onmessage = function (e) {
        console.log('Received from worker:', e.data);
    };

    worker.postMessage(10);
})