{"id":12660,"library":"webext-messenger","title":"Web Extension Messenger","description":"webext-messenger is a focused JavaScript library designed to streamline inter-component communication within browser extensions. It provides a robust framework for message passing between different parts of an extension, such as background scripts, content scripts, and web pages, as well as offscreen documents. The current stable version is 0.35.0, with frequent minor releases indicating active development, often introducing new features and internal optimizations. A key differentiator is its emphasis on minimizing external dependencies, aiming for a lightweight footprint, as evidenced by recent efforts to drop libraries like `p-retry` and `webextension-polyfill` for core functionality. This library simplifies complex messaging patterns, abstracting away the underlying browser `runtime.sendMessage` and `runtime.onMessage` APIs to offer a more developer-friendly interface for building robust and scalable browser extensions.","status":"active","version":"0.35.0","language":"javascript","source_language":"en","source_url":"https://github.com/pixiebrix/webext-messenger","tags":["javascript"],"install":[{"cmd":"npm install webext-messenger","lang":"bash","label":"npm"},{"cmd":"yarn add webext-messenger","lang":"bash","label":"yarn"},{"cmd":"pnpm add webext-messenger","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Provides a Promise-based wrapper for WebExtension APIs for cross-browser compatibility. While `webext-messenger` itself dropped it as a direct dependency in v0.32.0, many users still rely on it for consistent API access, and it's often a peer dependency for complex extensions.","package":"webextension-polyfill","optional":true}],"imports":[{"note":"The primary entry point is a default export, typically imported as `messenger`.","wrong":"const messenger = require('webext-messenger');","symbol":"messenger","correct":"import messenger from 'webext-messenger';"},{"note":"Named export for the custom error class, available since v0.33.1.","wrong":"import messenger from 'webext-messenger'; messenger.MessengerError; // incorrect direct access","symbol":"MessengerError","correct":"import { MessengerError } from 'webext-messenger';"},{"note":"While `messenger` is the default, this pattern can be used to access both default and named exports, e.g., `Messenger.default` and `Messenger.MessengerError`.","symbol":"* as Messenger","correct":"import * as Messenger from 'webext-messenger';"}],"quickstart":{"code":"/* === manifest.json (Manifest V3) === */\n{\n  \"manifest_version\": 3,\n  \"name\": \"Webext Messenger Demo\",\n  \"version\": \"1.0\",\n  \"permissions\": [\"activeTab\", \"scripting\"],\n  \"background\": {\n    \"service_worker\": \"background.js\"\n  },\n  \"content_scripts\": [\n    {\n      \"matches\": [\"<all_urls>\"],\n      \"js\": [\"content.js\"]\n    }\n  ],\n  \"host_permissions\": [\"<all_urls>\"]\n}\n\n/* === background.js === */\nimport messenger from 'webext-messenger';\n\nconst backgroundMessenger = messenger.init('background');\n\nbackgroundMessenger.onMessage('ping', (data, sender) => {\n  console.log('Background received ping:', data, 'from', sender.tab?.url);\n  return { response: 'pong from background', receivedData: data };\n});\n\nbackgroundMessenger.onMessage('logTabUrl', async (data, sender) => {\n  console.log('Background received logTabUrl request for tab ID:', data.tabId);\n  try {\n    const tab = await chrome.tabs.get(data.tabId);\n    return { url: tab.url };\n  } catch (error) {\n    console.error('Error getting tab:', error);\n    return { error: error.message };\n  }\n});\n\nconsole.log('Background script initialized with webext-messenger');\n\n/* === content.js === */\nimport messenger from 'webext-messenger';\n\nconst contentMessenger = messenger.init('content-script');\n\ncontentMessenger.onMessage('alertUser', (message) => {\n  alert('Message from background: ' + message);\n  return 'Alert shown!';\n});\n\nasync function sendPingToBackground() {\n  console.log('Content script sending ping...');\n  try {\n    const response = await contentMessenger.sendMessage('background', 'ping', { message: 'Hello from content!' });\n    console.log('Content received response from background:', response);\n  } catch (error) {\n    console.error('Content script failed to send ping:', error);\n  }\n}\n\nasync function requestBackgroundForTabUrl() {\n  console.log('Content script requesting current tab URL from background...');\n  try {\n    const tabId = await new Promise(resolve => chrome.tabs.getCurrent(tab => resolve(tab.id)));\n    const response = await contentMessenger.sendMessage('background', 'logTabUrl', { tabId: tabId });\n    console.log('Content received tab URL from background:', response.url);\n  } catch (error) {\n    console.error('Content script failed to request tab URL:', error);\n  }\n}\n\n// Run these after a short delay to ensure messenger is ready\nsetTimeout(() => {\n  sendPingToBackground();\n  requestBackgroundForTabUrl();\n}, 1000);\n\nconsole.log('Content script initialized with webext-messenger');","lang":"typescript","description":"This quickstart demonstrates basic inter-component communication between a background service worker and a content script using `webext-messenger`. It shows how to initialize messengers in different contexts, set up message listeners, and send messages with data, including handling responses and errors across extension components. The manifest is provided for a complete example."},"warnings":[{"fix":"If your extension relies on `browser` APIs, ensure `webextension-polyfill` is installed as a dependency in your project (`npm install webextension-polyfill`) and correctly initialized, or update your code to use `chrome` APIs directly where possible.","message":"The package made several changes regarding `webextension-polyfill` usage. It was explicitly used in v0.31.0 and then dropped as a direct dependency in v0.32.0. This means extensions relying on `webextension-polyfill` for the `browser` API object might need to explicitly install and manage it, or adapt to using `chrome` APIs directly if `webext-messenger` no longer polyfills internally.","severity":"breaking","affected_versions":">=0.32.0"},{"fix":"Review any parts of your extension where message sending might fail and rely on automatic retries. Test behavior thoroughly with v0.35.0+ to ensure it meets your fault tolerance requirements. Consider implementing custom retry logic if the new default behavior is insufficient.","message":"In v0.35.0, the package dropped `p-retry` and addressed 'retry bugs'. While intended as a fix, this change might alter the retry behavior of message sending for users who implicitly relied on `p-retry`'s specific logic. Applications with sensitive retry requirements should re-test message resilience.","severity":"gotcha","affected_versions":">=0.35.0"},{"fix":"When receiving messages in background scripts, always validate the `sender` origin and any received data, especially when messages originate from web pages (e.g., `sender.url` or `sender.origin`). Implement strict allow-lists for message types and data schemas.","message":"The introduction of 'web -> background messaging' in v0.33.0 means web pages can initiate messages to the background script. While a new feature, ensure proper input validation and origin checks are in place within your background script's `onMessage` listeners to prevent potential security vulnerabilities from untrusted web content.","severity":"breaking","affected_versions":">=0.33.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Ensure you are using `import messenger from 'webext-messenger';` at the top of your module. If in a CommonJS context (e.g., older Node.js scripts), you might need `const messenger = require('webext-messenger').default;` or transpile your code to ESM.","cause":"Attempting to use `messenger` without proper ESM import or in a CommonJS environment without transpilation.","error":"Uncaught ReferenceError: messenger is not defined"},{"fix":"Verify that the target script (`background.js`, `content.js`) is correctly registered in your `manifest.json`, is loaded in the browser, and has initialized `messenger.init()` with the correct name and an `onMessage` listener for the message type being sent. Ensure contexts can communicate (e.g., content script cannot directly message another content script).","cause":"A common browser extension error indicating that the target component (e.g., content script, background script) is either not loaded, not listening for messages, or not accessible from the sending context.","error":"Error: Could not establish connection. Receiving end does not exist."}],"ecosystem":"npm"}