experiment · shipped

Web Extensions Bridge

Wrapping webext-bridge for reliable extension messaging across browsers

the problem

Extension messaging behaves differently across browsers, and webext-bridge — the third-party library most extensions reach for — needed help to behave consistently outside Chrome

The concrete failure was object handling in Firefox. Objects passed through the messaging layer did not survive the trip the way they do in Chrome, so payloads had to be serialized and deserialized before being sent. Rather than patching that in each project, we wrapped the send call once:

function sendMessageWrapper<T extends Array<any>, U>(fn: (...args: T) => U) {
  return (...args: T): U => {
    if (typeof args[1] === 'object' && args[1] !== null) {
      args[1] = JSON.parse(JSON.stringify(args[1]))
    }

    return fn(...args)
  }
}

A round trip through JSON is a blunt instrument. It is also the smallest thing that made the same call work identically in every browser we shipped to, which is the trade a compatibility layer exists to make

what we built

A thin wrapper around the upstream webext-bridge library that smooths over those differences and ships a dedicated entry point for every extension context: background, content script, devtools, options, popup, and window. Each of our extensions imports the entry point for the context it runs in and gets the same messaging API everywhere, with improved TypeScript typings on top

The upstream library does the actual message routing; ours is the compatibility and packaging layer over it

what it cost

The upgrade from upstream version 5 to version 6 broke messaging in ways that were not obvious from the changelog, and our integration had to change to follow it. Upstream has not published since — 6.0.1 has stood since April 2023 — so the wrapper is now the layer that keeps this working for us

where it stands

Internal, never published. It is still used across our extensions wherever the low-level messaging layer is needed

Newer work sits a level above it: DecoExt turns the same plumbing into decorators, so a project declares the handler and lets the library wire the message bus. That is the approach we reach for now

Let's talk

Your contact info is sent!

We will contact you soon!