Communication between a Chrome extension and a web app
TypeScript
JavaScript

In developing Chrome extensions that interact with embedded web applications, establishing effective communication channels is crucial for seamless functionality. This article outlines techniques to facilitate robust event exchanges between a Chrome extension and a web app running within its iframe.
To facilitate clear communication, we start by defining the interface for the events used in our examples:
TypeScript
interface TSendMessageEvent { payload?: Record<string, string>; provider: string; type: string; }
Sending events from the web app to the extension
When sending events from the web app to the Chrome extension, the first step involves implementing a function to send messages:
TypeScript
const CHROME_EXTENSION_ID = 'abcdefghijklmnopqrstuvwxyzabcdef'; Â const sendMessageToExtension = async (event: TSendMessageEvent): Promise<void> => { if (!window.chrome?.runtime) { return; } Â try { await window.chrome.runtime.sendMessage(CHROME_EXTENSION_ID, event); } catch (error) { console.warn('Failed to communicate with the extension:', error); } }; Â sendMessageToExtension({ payload: { lorem: 'ipsum', }, provider: 'my-web-app', type: 'event-type-name', });
Next, the Chrome extension's service worker listens for these messages and relays them to active tabs:
TypeScript
chrome.runtime.onMessageExternal.addListener((event: TSendMessageEvent) => { if (event.provider !== 'my-web-app') { return; } Â chrome.tabs.query({}, tabs => { tabs.forEach(async tab => { if (!tab.id) { return; } Â try { await chrome.tabs.sendMessage(tab.id, event); } catch (error) { console.warn(`Tab ${tab.id} is not reachable`); } }); }); });
In the content script of the extension, the event is processed based on its type and origin:
TypeScript
chrome.runtime.onMessage.addListener((event: TSendMessageEvent) => { if (event.provider !== 'my-web-app' || event.type !== 'event-type-name') { return; } Â // Handle the event. });
Sending events from the Chrome extension to the web app
Conversely, events can be sent from the Chrome extension to the embedded web app using postMessage
:
TypeScript
const sendMessageToWebApp = (event: TSendMessageEvent) => { const elementMyAppFrame = window.document.querySelector<HTMLIFrameElement>('#my-app'); Â if (!elementMyAppFrame || !elementMyAppFrame.contentWindow) { return; } Â elementMyAppFrame.contentWindow.postMessage(event, '*'); }; Â sendMessageToWebApp({ payload: { lorem: 'ipsum', }, provider: 'my-chrome-extension', type: 'event-type-name', });
In the web app, handle the incoming messages securely within the iframe:
TypeScript
const handleMessage = ({ data }: TSendMessageEvent) => { const { payload, provider, type } = data; Â if (provider !== 'my-chrome-extension' || type !== 'event-type-name') { return; } Â // Handle the event. }; Â window.addEventListener('load', () => { window.addEventListener('message', handleMessage); }); Â window.addEventListener('unload', () => { window.removeEventListener('message', handleMessage); });
By following these structured approaches, you can ensure robust communication between a Chrome extension and an embedded web app, enhancing user experience through seamless interaction and data exchange. This integration not only simplifies workflows but also expands the capabilities of both the extension and the web app in tandem.