Comlink

A tiny RPC library that makes Web Workers enjoyable to use in JavaScript

Library
npm
v4.4.2
12,807 stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
56 /100 Fair
Development Activity 44
Maintenance 20
Community 60
Maturity 60
Momentum 40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
79 /100 Good
Architecture 80
Code Quality 75
Innovation 82
Learning Curve 80

Comlink is a 1.1kB library from Google Chrome Labs that removes the mental overhead of working with the postMessage API by turning WebWorker communication into an RPC-style interface built on ES6 Proxies. Instead of manually serializing messages and listening for responses, developers call functions and access properties on a wrapped worker proxy as if the worker’s exposed object were a local value.

Because keeping the main thread idle is critical for responsive UIs (especially on low-end mobile devices), Comlink makes it dramatically easier to offload real work — computation, parsing, image processing — into a worker thread without writing boilerplate message-passing code. It also works with Node.js worker_threads, SharedWorker, iframes, and cross-window messaging via the same wrap/expose model.

What You Get

  • Comlink.expose(value, endpoint?, allowedOrigins?) to publish an object, function, or class instance across a postMessage channel
  • Comlink.wrap(endpoint) to get an async proxy on the other side with all the original’s properties and methods
  • Comlink.transfer() and Comlink.proxy() for fine-grained control over structured cloning vs. transferable objects vs. live callback proxies
  • Custom transfer handlers for serializing non-cloneable values like DOM Events
  • Support for WebWorkers, SharedWorkers, Node.js worker_threads, iframes, and cross-window messaging

Common Use Cases

  • Offloading CPU-intensive computation (image processing, data crunching, parsing) off the main UI thread
  • Building responsive web apps on low-end mobile devices where a blocked main thread causes visible jank
  • Structuring worker-based architectures without hand-rolling message-id correlation and event listener boilerplate
  • Communicating between an iframe and its parent window, or between browser windows, using the same proxy-based API as WebWorkers

Under The Hood

Architecture - The entire library is three TypeScript files: src/comlink.ts (~650 lines) implements the core expose/wrap proxy machinery and transfer-handler registry, src/protocol.ts defines the internal message protocol types, and src/node-adapter.ts adapts Node.js worker_threads ports to the same postMessage-like endpoint interface used in browsers. A JS Proxy intercepts property access and method calls on the wrapped object, translating them into structured postMessage calls and awaiting a correlated response message.

Tech Stack - Roughly even split of TypeScript and JavaScript, built and bundled to ESM, UMD, and a minified global build (dist/umd/comlink.js) so it can be loaded via importScripts inside a worker, import in a module context, or a plain <script> tag. No runtime dependencies at all — the entire implementation rests on native Proxy, postMessage, and structured cloning.

Code Quality - Tests live under tests/ (worker.comlink.test.js, iframe.comlink.test.js, same_window.comlink.test.js, cross-origin.comlink.test.js, two-way-iframe.comlink.test.js) and are run in real browser contexts via Karma/Playwright-style runners to exercise actual postMessage behavior rather than mocks, which is essential given how environment-dependent this code is. TypeScript types for Remote<T> are maintained on a best-effort basis, as the README itself notes some proxy/RPC type shapes are fundamentally hard to express precisely.

API Design - The expose()/wrap() pairing is minimal and consistent: whatever object you expose, you get back a promise-returning mirror of it, with the rule of thumb “if you’re using the proxy, await it.” This low-ceremony design is why Comlink is frequently reached for over hand-written postMessage protocols — there’s essentially no boilerplate for the common case, with transfer()/proxy() available as escape hatches for the less common ones.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers