Inter-component messaging patterns for Chrome Extensions covering background ↔ content script ↔ popup ↔ side panel communication, ports, message passing, state synchronization, and error handling. Essential for multi-component extensions.
View on GitHubfrancanete/fran-marketplace
chrome-extension-expert
chrome-extension-expert/skills/component-communication/SKILL.md
January 20, 2026
Select agents to install to:
npx add-skill https://github.com/francanete/fran-marketplace/blob/main/chrome-extension-expert/skills/component-communication/SKILL.md -a claude-code --skill component-communicationInstallation paths:
.claude/skills/component-communication/# Chrome Extension Component Communication
## Overview
Chrome extensions have isolated components that must communicate via message passing:
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Side Panel │ │ Popup │ │ Options Page │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└───────────────────────┼───────────────────────┘
│
┌────────────┴────────────┐
│ Background Service │
│ Worker │
└────────────┬────────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
┌────────┴────────┐ ┌────────┴────────┐ ┌────────┴────────┐
│ Content Script │ │ Content Script │ │ Content Script │
│ (Tab 1) │ │ (Tab 2) │ │ (Tab 3) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
---
## One-Time Messages
### Background ↔ Content Script
**Content Script → Background:**
```javascript
// content.js
const response = await chrome.runtime.sendMessage({
type: 'SAVE_DATA',
data: { url: window.location.href }
});
console.log('Response:', response);
```
**Background → Content Script:**
```javascript
// background.js
const response = await chrome.tabs.sendMessage(tabId, {
type: 'GET_PAGE_DATA'
});
```
**Background Listener:**
```javascript
// background.js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
// sender.tab exists if from content script
if (sender.tab) {
console.log('From tab:', sender.tab.id, sender.tab.url);
}
switch (message.type) {
case 'SAVE_DATA':
saveData(message.data)
.then(result => sendResponse({ success: true, result }))
.catch(error => sendResponse({