A simple project showcasing real-time inter-tab communication using browser's Broadcast Channel API and PostMessage API. This project features a custom-built advanced-broadcast-message tool that extends the browser's native Broadcast Channel API with async/await support, enabling promise-based communication patterns between browser tabs.
This demo illustrates how multiple browser tabs can stay synchronized with a single source of truth, where one tab acts as the primary hub performing all actions and broadcasting state changes to other tabs.
This demo implements a primary hub pattern for inter-tab communication, where:
- Primary Tab: Acts as the single source of truth, performs all actions, and stores all state updates
- Secondary Tabs: Automatically sync their state with the primary tab through broadcast messages
- Real-time Sync: Changes made in the primary tab are instantly reflected across all open tabs
We demonstrate this pattern using a simple Content Management System (CMS) use case, where form changes in one tab are immediately visible in preview panes across all tabs.
The project uses two communication mechanisms:
-
Advanced Broadcast Message (
advanced-broadcast-message): A custom-built tool that extends the browser's native Broadcast Channel API with async/await support. This enables promise-based, request-response patterns for inter-tab communication, making it easier to handle state synchronization and initialization requests. -
PostMessage API (
advanced-post-message): Enhanced postMessage API for iframe communication
The native Browser Broadcast Channel API only supports one-way message passing. This project uses a custom advanced-broadcast-message tool that introduces:
- Promise-based communication: Send messages and await responses
- Request-response pattern: Request initial state and receive it asynchronously
- Type-safe messaging: Full TypeScript support for message payloads
- Error handling: Built-in error suppression and promise rejection handling
This allows for cleaner, more intuitive code patterns like:
// Request initial state and await the response
const response = await advBroadcastMessage
.send<OnInitResponsePayload>(COMMUNICATION_EVENTS.INIT);
// Handle the response
setState(response.cmsData);┌─────────────────┐
│ Primary Tab │ ← Single Source of Truth
│ (CMS Form) │
└────────┬────────┘
│
├─── Broadcast Channel ───┐
│ │
│ ▼
┌────┴────┐ ┌─────────────┐
│ Tab 2 │ │ Tab 3 │
│ (Sync) │ │ (Sync) │
└─────────┘ └─────────────┘
- Initialization: When a new tab opens, it requests the current state from the primary tab via broadcast messages
- State Updates: When the primary tab makes changes, it broadcasts updates to all listening tabs
- Synchronization: Secondary tabs receive updates and automatically sync their local state
- Iframe Communication: The preview pane (iframe) receives updates via postMessage API
- Node.js 18+
- pnpm (or npm/yarn/bun)
# Install dependencies
pnpm install# Start the development server
pnpm devOpen http://localhost:6576 in your browser.
# Build the application
pnpm build
# Start production server
pnpm start- Open the Primary Tab: Navigate to
http://localhost:6576- this is your primary hub - Open Secondary Tabs: Open
http://localhost:6576/new-tabin additional browser tabs - Make Changes: Edit the CMS form fields in the primary tab
- Observe Sync: Watch as changes instantly appear in all secondary tabs' preview panes
/- Primary tab with CMS form and preview pane/new-tab- Secondary tab that syncs with the primary tab/user-website- Preview iframe that displays the CMS content
This project showcases a custom advanced-broadcast-message library that enhances the browser's Broadcast Channel API. Unlike the native API which only supports one-way messaging, this tool enables:
- Async/await patterns: Send messages and await responses using promises
- Request-response flow: Request data from other tabs and receive responses
- Type safety: Full TypeScript support with typed payloads and responses
- Event listeners: Register handlers that can return responses to requests
The project uses a structured event system:
INIT: Request initial state from the primary tab (uses async/await pattern)ON_CHANGE: Broadcast state changes to all tabs (one-way broadcast)
communication-channel.ts: Initializes broadcast and postMessage managerscommunication-channel.constant.ts: Defines channel IDs and event namescommunication-channel.type.ts: TypeScript types for payloads
cms-modle.constant.ts: Default CMS data and layout optionscms-module.type.ts: TypeScript types for CMS data structure
CMSForm: Primary form component that acts as the source of truthPreviewPane: Displays the preview iframeNewTabPage: Secondary tab that syncs with primary tab
The async/await pattern enables clean request-response flows:
Primary Tab (CMSForm)
│
├─→ handleChange()
│ │
│ ├─→ advPostMessageParent.send() → iframe (PreviewPane)
│ └─→ advBroadcastMessage.send() → all tabs (one-way broadcast)
│
└─→ advBroadcastMessage.on('INIT', async () => {
return { cmsData: formData }; // Returns promise response
})
Secondary Tab (NewTabPage)
│
├─→ await advBroadcastMessage.send('INIT') → requests & receives state
│ (Uses async/await to get response from primary tab)
│
└─→ advBroadcastMessage.on('ON_CHANGE') → receives updates
Preview Iframe (UserWebsite)
│
├─→ await advPostMessageChild.send('INIT') → requests state
└─→ advPostMessageChild.on('ON_CHANGE') → receives updates
Here's how the advanced broadcast message tool enables async/await patterns:
Primary Tab (listens for INIT requests):
advBroadcastMessage?.on<undefined, OnInitResponsePayload>(
COMMUNICATION_EVENTS.INIT,
async () => {
return { cmsData: formData }; // Returns response
}
);Secondary Tab (requests initial state):
const response = await advBroadcastMessage
?.send<OnInitResponsePayload>(COMMUNICATION_EVENTS.INIT);
// response.cmsData is now availableadvanced-broadcast-message: Custom-built tool that extends the browser's Broadcast Channel API with async/await support, enabling promise-based request-response patterns for inter-tab communicationadvanced-post-message: Enhanced postMessage API for iframe communication enabling promise-based request-response patterns for inter-tab communication
blog-inter-tab-communication/
├── app/
│ ├── page.tsx # Primary tab (CMS form + preview)
│ ├── new-tab/
│ │ └── page.tsx # Secondary tab (syncs with primary)
│ └── user-website/
│ └── page.tsx # Preview iframe content
├── components/
│ ├── CMSForm/ # CMS form component
│ ├── PreviewPane/ # Preview pane wrapper
│ └── ui/ # Reusable UI components
├── lib/
│ ├── communication-channel/ # Communication utilities
│ └── cms-module/ # CMS data types and constants
└── package.json
This pattern is useful for:
- Multi-tab applications: Keep state synchronized across tabs
- CMS/Admin panels: Edit in one tab, preview in others
- Real-time dashboards: Share data across multiple views
- Collaborative tools: Broadcast changes to all participants
- Development tools: Live preview across multiple windows
- Open the primary tab at
http://localhost:6576 - Open 2-3 additional tabs at
http://localhost:6576/new-tab - Make changes to the CMS form in the primary tab
- Observe real-time updates in all secondary tabs
- Try opening tabs in different browser windows to see cross-window communication
This project is for demonstration purposes.
This project demonstrates a custom advanced-broadcast-message library that solves a key limitation of the native Browser Broadcast Channel API: the lack of async/await support.
The native API only supports one-way message broadcasting, making it difficult to implement request-response patterns. This custom tool adds:
- Promise-based messaging with async/await
- Request-response communication patterns
- Type-safe message handling
- Error handling and suppression
This enables cleaner, more intuitive inter-tab communication patterns that feel natural in modern JavaScript/TypeScript codebases.
Built with Next.js and a custom advanced broadcast messaging tool.