# webext-bridge: one messaging API for every context in a browser extension

> webext-bridge is a small TypeScript library that gives a browser extension a single way to talk between its background script, content script, devtools panel, popup, options page and injected page scripts, hiding the fact that each of those contexts has a different native messaging mechanism.

**serversideup/webext-bridge** — 💬 Messaging in Web Extensions made easy. Batteries included.

- Repository: https://github.com/serversideup/webext-bridge
- Website: https://serversideup.net/open-source/webext-bridge
- Stars: 714 · Forks: 68
- Language: TypeScript
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/serversideup-webext-bridge

## The problem is that each context speaks a different dialect

webext-bridge is a small library that provides a simple and consistent API for sending and receiving messages between the different parts of a web extension, and the list of parts in the README is the background script, the content script, devtools, the popup, options and window contexts.

That list is the whole problem statement. In the extension platform, each of those contexts has a different native way of sending a message, and each has its own restrictions. A content script cannot open a long-lived connection to the background page in the same way, a devtools panel has its own channel, and an injected page script is in a different JavaScript world from the extension entirely. Code that keeps data in sync across them ends up with a pile of small adapters.

The library's claim is that it reduces the headache and simplifies the effort of keeping data in sync, with batteries included, and it is described as a tiny library. It is MIT licensed, the primary language is TypeScript, the default branch is main, and the last push was on 2026-09-24. The package version is 6.0.1, and there are no GitHub releases, so the npm tag is the version.

The project has a recent change of ownership that the README marks as important. webext-bridge has joined the Server Side Up family of open source projects, and the announcement is linked as a discussion. That matters for anyone evaluating it, because a small library in a two-person company portfolio is a different maintenance story from an independent project, and the project now offers professional help alongside the open source code.

## Seven entry points, one per context

The package manifest is where the design becomes concrete, because the exports map has one subpath per extension context.

```json
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/index.cjs"
    },
    "./background": {
      "import": "./dist/background.js",
      "require": "./dist/background.cjs"
    },
```

And the same pattern repeats for the other contexts, with a subpath each for content-script, devtools, options, popup and window, and each one pointing at both an ESM file and a CommonJS file. Every subpath has a matching entry in the typesVersions map, so the type declarations resolve per subpath rather than only for the package root.

That is the interesting implementation detail. A messaging library where every context needs different glue cannot be a single import, so the shape of the package is one module per context, and the fact that the build script compiles the same list of entry points confirms it:

```json
    "build": "tsup src/index.ts src/background.ts src/content-script.ts src/devtools.ts src/options.ts src/popup.ts src/window.ts --format esm,cjs --dts",
```

So the source tree is a directory of files named after the contexts, the bundler is tsup producing both module formats plus declarations, and the exports map wires the compiled output back to the names you import. A developer writes a background page that imports from the background subpath and a content script that imports from the content-script subpath, and both get the same messaging API underneath.

The type field is module, so the package is ESM first, and the CommonJS output is there for the extension contexts that are still CommonJS. That pairing is deliberate, because a browser extension with a background service worker and a content script often has one of each.

## Building it, and the release script

The scripts section of the manifest is three entries, and the release one is worth reading because it shows how the package is published.

```json
    "watch": "npm run build -- --watch",
    "release": "bumpp --commit --push --tag && npm run build && npm publish"
```

The watch script is the build with a watch flag, so the development loop is a single command and the same bundler produces the output.

The release script is more interesting. It runs bumpp with commit, push and tag, so the version bump becomes a commit, a push and a git tag in one step, and only then does it build and publish. The order matters, because the published artefact is built after the tag is pushed, which means the tag and the npm release are meant to correspond.

The type declarations are generated in the same build with the dts flag, so the dist/index.d.ts and the per-context declaration files are produced from the TypeScript sources rather than hand written.

The repository layout supports that. There is a src/ directory, a docs/ directory, a tsconfig.json, an eslint configuration in .eslintrc.json, a pnpm-lock.yaml so the project uses pnpm, a CHANGELOG.md, a .npmignore for the published file set and a .eslintignore.

One detail in the manifest is a small sign of the project's history. The repository field points at a fork under a different account, and the author is listed as Neek Sandhu, while the project itself is now maintained under the Server Side Up organisation. The code and the manifest were carried across the handover rather than rewritten, which is normal and means the npm author field may not match the current maintainers.

## Where the project documentation and the support model sit

The README is mostly navigation and project information rather than usage, and that is a choice worth noting for a library.

The resources list is short. There is a Discord for support from the community and the team, the GitHub repository for source code, bug reports and project management, and a Get Professional Help link offering video and screen sharing help directly from the core contributors. The website overview and the deeper documentation links are present in the source but commented out in the rendered page, which suggests the project page was being reorganised around the time of the handover.

The contribution guidance is unusually thorough for a library this small. There are five named routes, and each one has a specific destination. Bug reports go to a GitHub issue template. Feature requests go to a discussion thread rather than an issue, so the two do not get mixed. Documentation changes go to a documentation change link inside the docs directory. Community support goes to GitHub Discussions or Discord. And security issues go to a responsible disclosure policy hosted on Notion, which is the one route that is deliberately kept out of the public tracker.

The README also explains what the project is, which is that all of their software is free and open to the world, and that this cannot be brought to you without the financial backing of their sponsors. The sponsor list is in the page itself, with Sevalla as a platinum sponsor, GiGa infosystems as a silver sponsor, and Depot and Docker Hub as infrastructure sponsors. A library that depends on a paid hosting sponsor for its own infrastructure has a different risk profile from one that does not, and the project is upfront about the arrangement rather than hiding it behind a donate button.

## Where webext-bridge is the wrong choice

The first wrong case is an extension with one context. If you are writing a content script that never messages a background page, there is nothing to bridge, and the library is a dependency that solves a problem you do not have. The seven entry points exist because the contexts are genuinely different, and with one context there is no difference to bridge.

The second is a project that needs its own protocol anyway. If you are streaming large payloads, or you need request and response correlation with timeouts, or you want a typed schema for your messages, the library's consistent API is the starting point rather than the solution. A message bus that cannot express those things is a layer you will end up extending.

The third is a team that needs a long-term support commitment. There are no GitHub releases, the version is 6.0.1, and the project is now inside a two-person commercial company that also sells consulting. That combination is not a problem in itself, and the professional help option is a genuine benefit, but it is a different thing from a library with a broad community or a foundation backing it.

The fourth is a team that has already standardised on the platform's native messaging. Chrome and Firefox both offer runtime messaging, and if your extension already wraps it in your own thin layer with types, adding a second abstraction underneath is a second thing to learn and a second thing to debug when a message goes missing.

The alternative worth weighing is the native API plus your own typed wrapper, which is what a lot of extensions end up with anyway. The difference is that you own the semantics, including the ones the library deliberately leaves open, such as what happens to a message when the receiving context is not currently loaded.

## Conclusion

Adopt webext-bridge if you are building a browser extension with more than one context and the native messaging APIs are eating your time, since the library gives every context the same interface and ships a per-context entry point for each. Keep away if your extension is a single content script that never talks to a background page, because there is nothing to bridge. Before you upgrade, read the changelog, since the package is at version 6.0.1 with no tagged GitHub releases and the major version suggests the entry point layout has changed, and note the project is now part of the Server Side Up family with commercial support offered alongside it.

## FAQ

### What is webext-bridge and what problem does it solve?

It is a small TypeScript library that provides a simple and consistent API for sending and receiving messages between the different parts of a web extension, such as the background script, content script, devtools, popup, options and window contexts. It is intended to reduce the effort of keeping data in sync across those parts.

### How is the webext-bridge package structured?

It has one export subpath per extension context, each pointing at both an ESM and a CommonJS build, plus a matching declaration file per subpath. The build script compiles src/index.ts and one file per context with tsup in both module formats with generated types, and the package type is module.

### How are webext-bridge releases cut?

The release script runs bumpp with commit, push and tag, then builds and publishes to npm, so the version bump becomes a commit, a push and a git tag before the package is published. The repository has no GitHub releases, so the npm version is the version of record.

### How do I report a security issue in webext-bridge?

Security issues go to the responsible disclosure policy, which is linked from the README and hosted on Notion, and deliberately kept out of the public tracker. Bug reports go to the issue template, feature requests to a discussion thread, and documentation changes to a link inside the docs directory.

## Sources

- [Issues](https://github.com/serversideup/webext-bridge/issues)
- [License: MIT](https://github.com/serversideup/webext-bridge/blob/main/LICENSE)
- [Project website](https://serversideup.net/open-source/webext-bridge)
- [README](https://github.com/serversideup/webext-bridge/blob/main/README.md)
- [serversideup/webext-bridge on GitHub](https://github.com/serversideup/webext-bridge)

---

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