Get started

Cross your first boundary.

Install one zero-dependency module, create a { type, data } envelope, and use the same shape on both sides of the wire.

1. Install and import

npm install js-message
import Message from 'js-message';

Version 3.1.1 keeps one native JavaScript implementation. Node 22.13+ can load it through import or synchronous require(), with no runtime dependency or compile step.

js-message works with bundlers and without a bundler.

Bundlers resolve the bare js-message import normally. Native browser ESM resolves it through a standard import map, with no build or transpilation step.

Native browser ESM

<script type="importmap">
{
  "imports": {
    "js-message": "./node_modules/js-message/index.js",
    "js-message/": "./node_modules/js-message/"
  }
}
</script>
<script type="module">
  import Message from 'js-message';

  console.log(new Message({ type: 'page.ready' }).JSON);
</script>

The map is complete because js-message has zero runtime dependencies, so no dependency or scope entries are needed. Its paths are relative to the HTML document. Serve the app over HTTP(S), and configure the server to expose these node_modules files; file:// is not supported.

2. Create and serialize

const message = new Message();

message.type = 'order.created';
message.data = {
    id: 'ord_481',
    total: 27.50
};

transport.send(message.JSON);

message.JSON is a string containing only type and data. Other properties you add to the instance stay local.

No payload needed?

new Message({ type: 'ping' }) is valid. data remains undefined and the wire form stays {"type":"ping"}.

3. Load and branch

const incoming = new Message();
incoming.load(receivedValue);

if (incoming.type === 'error') {
    console.error(incoming.data.err);
} else {
    route(incoming.type, incoming.data);
}

load() accepts a JSON string or an existing object and copies the fields that are available. Malformed or uncoercible input becomes a recoverable type: 'error' message instead of escaping from load().