# Workerize moves a module into a Web Worker and proxies its exports

> Workerize is a small JavaScript library that loads a module or a string of code into a Web Worker and turns every exported function into an asynchronous proxy. It suits codebases that want a background thread without writing the worker message plumbing by hand.

**developit/workerize** — GitHub describes it as 🏗️ Run a module in a Web Worker.. The repository metadata lists JavaScript as its primary language. This article stays within the project description and details documented in the GitHub repository README.

- Repository: https://github.com/developit/workerize
- Website: https://github.com/developit/workerize-loader
- Stars: 4,374 · Forks: 86
- Language: JavaScript
- License: not declared
- Published: 2026-08-13 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/developit-workerize

## One function call replaces the whole worker message channel

Moving code off the main thread usually means writing a message channel by hand: serialise arguments into postMessage, rebuild them inside the worker, post a reply back, and correlate each reply with the call that produced it. Workerize inverts that. You hand it a module or a string of source, and every function that module exports becomes a method on the returned object. Calling worker.add(3, 9) looks like an ordinary function call and returns a promise instead of a number.

That inversion is the whole product. The runtime bundles its own RPC layer into your application rather than asking you to construct a Worker and attach listeners, so the cost of moving a computation off the main thread collapses to a single import and one call. The README describes the exports as "automatically reflecting exported functions as asynchronous proxies", which is the accurate summary: the object you hold on the main thread is a stand-in, not the module itself.

If the functions you export are already async, the signature does not change. Synchronous functions work too, and the README is direct that the design "works beautifully with async/await", meaning you await a call that never had a synchronous form to begin with.

## Pass a string of ES module source, not a file path

The usage example does not import a module by path. It passes a template literal containing export statements straight into workerize, which turns that text into the body of a worker. The README puts it plainly: pass either a function or a string containing code. Whatever the entry resolves to, the module still has to be self-contained, because nothing from your page's scope reaches it.

Install it as an ordinary dependency:

```sh
npm install --save workerize
```

Then call it with your source. This is the README's own example, blocking the worker thread for half a second so the asynchrony is visible in the output:

```js
let worker = workerize(`
	export function add(a, b) {
		// block for half a second to demonstrate asynchronicity
		let start = Date.now();
		while (Date.now()-start < 500);
		return a + b;
	}
`);

(async () => {
	console.log('3 + 9 = ', await worker.add(3, 9));
	console.log('1 + 2 = ', await worker.add(1, 2));
})();
```

Two calls run in order but neither blocks the page. The while loop pins the worker thread for 500 milliseconds each time, and the console output still arrives.

## Where Webpack users are sent to loader.js instead

The first thing in the README is not an installation note, it is a redirect. A banner at the top points Webpack users at workerize-loader. Workerize ships a loader.js file, and package.json declares it under a loader key, so the package is bundler-aware even though the core library does not need one.

The distinction matters when you read the homepage field in package.json, which points at the workerize-loader repository rather than this one. The ecosystem around this code was split into two packages, and the runtime you install by default is the smaller half. If your build pipeline is Webpack, the README's answer is not this package at all.

For anyone on a different bundler, the src/index.js entry named in package.json is the place the logic lives, and dist/workerize.js plus dist/workerize.m.js are the published builds, produced by microbundle from the build script. Those three entry fields are the only packaging surface the repository declares.

## 800 bytes of gzipped ES3 is both the pitch and the constraint

The size claim is specific: 800 bytes gzipped, and the output targets ES3. That combination tells you what kind of code the runtime expects to be running inside the worker, since ES3 is a deliberately conservative syntax target aimed at old engines rather than anything a modern bundler targets by default.

Smallness is the reason to accept the restrictions. A worker payload has to be fetched or built before it can do anything, and 800 bytes makes that cost hard to notice. The RPC layer is included rather than left to you, which is why the figure covers the transport and not just a wrapper.

What 800 bytes cannot cover is a richer protocol. The README makes no mention of transferring typed arrays, sharing memory through SharedArrayBuffer, or streaming partial results, and there is no documented API surface for any of it. Arguments and return values cross the boundary through the same channel the runtime already set up, so what your function receives is whatever survives that trip.

## Exported functions see only what the worker bundle gives them

This is the failure mode to plan around. Because you pass a module or a source string, the code inside the worker has no access to your page's variables unless those variables are also present in that module. A function that closes over an application-level object, reads a global set by your framework, or touches document has nothing to reach for on the other side.

The example makes the rule visible by accident. The exported add function touches only Date.now and its own parameters, and it returns a primitive. Numbers survive a worker boundary without ceremony. Pass an object graph with methods on it and you are depending on structured clone semantics you will not find spelled out anywhere in the README, which documents no error handling for a rejected call and no timeout.

A rejected promise is also the only signal the design offers. If the function inside the worker throws, the caller sees whatever the runtime surfaces on the promise, and the README gives no example of that path. If you need per-call timeouts or retry logic, you are writing that layer yourself on top of the proxy.

## The test script exits zero without running anything

The package.json test script is worth reading before you rely on this code:

```json
"test": "echo \"Error: no test specified\" && exit 0"
```

It prints a message and exits successfully. There is no suite behind it, and the repository root holds .travis.yml alongside a demo.html, src/ and loader.js, so continuous integration has nothing to verify there.

The version fields disagree in a way that tells you where the project stopped. package.json reads 0.1.8, while the newest published release is 0.1.7 from 2018-02-01, following 0.1.6 and 0.1.5 the same week and the week before. Treat the library as frozen at an 800 byte ES3 design and verify behaviour against the copy your lockfile pins, not against the number in the repo.

## What comlink-style proxies do differently

The closest widely known alternative is Comlink, which also exposes worker functions as promise-returning methods but requires you to construct the Worker yourself with new Worker(new URL('./worker.js', import.meta.url)), postMessage a MessagePort at start-up, and wrap the remote end in Comlink.wrap. Workerize removes those three steps and the explicit URL plumbing, at the cost of a fixed RPC layer you do not control.

The difference is where the transport lives. Comlink delegates serialisation to the browser's own postMessage and leaves proxying to a library you can read, extend or bypass. Workerize hides the transport inside an 800 byte bundle and gives you no documented hook into it, so a case the bundled protocol does not handle has no escape hatch short of dropping to a raw Worker.

If you need shared memory, streaming or explicit control of the channel, take that route and write the plumbing. If you have a pure function or two that must not block the page, Workerize removes the parts of the job you would otherwise do by hand, and the README's example is short enough to verify the promise behaviour in a scratch file.

## Conclusion

Adopt Workerize if you have one or two pure functions you want off the main thread and you can accept an ES3 worker bundle that stopped shipping in 2018, and vendor it under its MIT terms with a pinned version. Do not adopt it for a project that needs structured clone of live objects, SharedArrayBuffer, streaming results or per-call timeouts, because none of that has a documented path in this package. Check first whether your module touches DOM globals or closes over page-level state, since a function inside the worker has no access to your scope, and confirm the promise rejects rather than hangs when the worker code throws. If either answer is no, write the Worker and the message channel yourself.

## FAQ

### What is Workerize and what does it do?

Workerize is a JavaScript library that moves a module into a Web Worker and reflects the module's exported functions as asynchronous proxies, so each call returns a promise instead of a value. The README states the runtime is 800 bytes of gzipped ES3.

### How do I install Workerize and run my first worker function?

Run npm install --save workerize, then pass a string of source containing export statements to workerize() and await the resulting methods. The README's example exports an add function that blocks the worker for half a second and returns a + b.

### Does Workerize work with Webpack?

The README directs Webpack users to workerize-loader instead, and package.json declares a loader field pointing at loader.js inside the workerize package. Workerize itself ships dist/workerize.js and dist/workerize.m.js builds produced by microbundle.

### Can the functions I export in Workerize use my app's variables and DOM?

The README documents no mechanism for sharing page state with the module you pass in, and its example touches only Date.now and its own parameters. Nothing describes transferring live objects, and no error handling is documented for a call that fails inside the worker.

### Is Workerize still maintained?

The last push to the default branch was on 2021-03-04, and the newest published release is 0.1.7 from 2018-02-01, while package.json reads version 0.1.8. The repository is MIT licensed.

## Sources

- [Official documentation](https://github.com/developit/workerize-loader)
- [Official README](https://github.com/developit/workerize#readme)
- [Project repository](https://github.com/developit/workerize)
- [Release notes](https://github.com/developit/workerize/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/developit-workerize
