Installation
MV2 MV3 Chrome Firefox Safari
Overview
@webext-core/messaging
a simplified, type-safe wrapper around the web extension messaging APIs. It also provides a similar interface for communicating with web pages or injected scripts.
@webext-core/proxy-service
for a more DX-friendly approach to executing code in the background script.Installation
NPM
pnpm i @webext-core/messaging
import { defineExtensionMessaging } from '@webext-core/messaging';
CDN
curl -o messaging.js https://cdn.jsdelivr.net/npm/@webext-core/messaging/lib/index.global.js
<script src="/messaging.js"></script>
<script>
const { defineExtensionMessaging } = webExtCoreMessaging;
</script>
Basic Usage
First, define a protocol map:
interface ProtocolMap {
getStringLength(data: string): number;
}
Then call defineExtensionMessaging
, passing your ProtocolMap
as the first type parameter.
Export the sendMessage
and onMessage
methods. These are what the rest of your extension will use to pass messages around.
import { defineExtensionMessaging } from '@webext-core/messaging';
interface ProtocolMap {
getStringLength(data: string): number;
}
export const { sendMessage, onMessage } = defineExtensionMessaging<ProtocolMap>();
Usually the onMessage
function will be used in the background and messages will be sent from other parts of the extension.
import { onMessage } from './messaging';
onMessage('getStringLength', message => {
return message.data.length;
});
Sending Messages to Tabs
You can also send messages from your background script to a tab, but you need to know the tabId
.
import { onMessage } from './messaging';
onMessage('getStringLength', message => {
return message.data.length;
});
Window Messaging
Inside a content script, you may need to communicate with a webpage or an injected script running in the page's JS context. In this case, you can use defineWindowMessenger
or defineCustomEventMessenger
, which use the window.postMessage
and CustomEvent
APIs respectively.
import { defineWindowMessaging } from '@webext-core/messaging/page';
export interface WebsiteMessengerSchema {
init(data: unknown): void;
somethingHappened(data: unknown): void;
}
export const websiteMessenger = defineWindowMessaging<WebsiteMessengerSchema>({
namespace: '<some-unique-string>',
});
Which one should I use?
Note the namespace option. Only messengers of the same type (window vs custom event) and same namespace will communicate. This prevents accidentially reacting to messages from the page or from another extension. Usually, it should be a unique string for your extension. The easiest method is to set it to browser.runtime.id
, but if you're injecting a script, webextension-polyfill
will not be available in the page context and you'll have to use something else or hardcode the extension's ID.
The messenger object can be used in the same way as the extension messenger, with sendMessage
and onMessage
.
Here, we're injecting a script, initializing it with data, and allowing the script to send data back to our content script.
import { websiteMessenger } from './website-messenging';
const script = document.createElement('script');
script.src = browser.runtime.getUrl('/path/to/injected.js');
document.head.appendChild(script);
script.onload = () => {
websiteMessenger.sendMessage("init", { ... });
}
websiteMessenger.onMessage("somethingHappened", (data) => {
// React to messages from the injected script
});