Skip to content

lopatnov/browser-tab-ipc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

69 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

browser-tab-ipc

Lightweight cross-tab messaging library for TypeScript / JavaScript. Exchange messages between browser tabs with automatic transport selection and graceful fallback.

npm downloads npm version License GitHub issues GitHub stars


Browser Support

Transport Chrome Firefox Safari Edge Notes
BroadcastChannel 54+ 38+ 15.4+ 79+ Default — fastest, same-origin
SharedWorker 4+ 29+ 16+ 79+ Cross-origin capable
SessionStorage all all all all Universal fallback

Table of Contents


Installation

npm:

npm install @lopatnov/browser-tab-ipc

yarn:

yarn add @lopatnov/browser-tab-ipc

CDN (UMD, no bundler required):

<script src="https://lopatnov.github.io/browser-tab-ipc/dist/library.umd.min.js"></script>

Quick Start

import {BrowserTabIPC} from '@lopatnov/browser-tab-ipc';

const ipc = new BrowserTabIPC();

// Listen for messages from other tabs
ipc.message((data) => {
  console.log('Received:', data);
});

// Connect and send
await ipc.connect();
await ipc.postMessage({event: 'tab-opened', tabId: crypto.randomUUID()});

Open the same page in multiple tabs — every tab receives the message instantly.


How It Works

BrowserTabIPC tries each transport in order and uses the first one that connects successfully:

BroadcastChannel  →  SharedWorker  →  SessionStorage
    (fastest)          (flexible)       (always works)

You can override this by specifying a single transport or a custom fallback list via ConnectionOptions.


API Reference

class BrowserTabIPC

BrowserTabIPC extends Node's EventEmitter and provides a clean connect / send / receive interface.

new BrowserTabIPC(options?: ConnectionOptions)

Creates an instance. Options set here apply to all subsequent connect() calls.

import {BrowserTabIPC, TransportType} from '@lopatnov/browser-tab-ipc';

// Default — auto-selects from all three transports
const ipc = new BrowserTabIPC();

// Force a single transport
const ipc = new BrowserTabIPC({
  transportTypes: TransportType.broadcastChannel,
});

// Custom fallback chain
const ipc = new BrowserTabIPC({
  transportTypes: [TransportType.sharedWorker, TransportType.sessionStorage],
  sharedWorkerUri: '/ipc-worker.js',
});

connect(options?: ConnectionOptions): Promise<ConnectionState>

Establishes the connection. Options passed here are merged with constructor options.

const state = await ipc.connect({
  sharedWorkerUri: '/dist/ipc-worker.js',
  storageKey: 'my-app-channel',
  storageExpiredTime: 30_000,
});

console.log(state.connected); // true
console.log(state.type); // e.g. TransportType.broadcastChannel

disconnect(): Promise<ConnectionState>

Closes the active connection and cleans up all listeners and timers.

const state = await ipc.disconnect();
console.log(state.connected); // false

postMessage(message: any): Promise<void>

Broadcasts a serializable value to all connected tabs.

await ipc.postMessage('ping');
await ipc.postMessage({type: 'STORE_UPDATE', payload: {count: 42}});

Event subscription methods

Method Trigger
message(callback) A message was received from another tab
connected(callback) Connection established successfully
connectionError(callback) Connection attempt failed
disconnected(callback) Connection was closed
ipc.message((data) => console.log('Message:', data));
ipc.connected((state) => console.log('Connected via', TransportType[state.type!]));
ipc.connectionError((state) => console.error('Connection failed:', state.error));
ipc.disconnected(() => console.log('Disconnected'));

You can also use the EventEmitter API directly with the exported event name constants:

import {EventMessage, EventConnected, EventConnectionError, EventDisconnected} from '@lopatnov/browser-tab-ipc';

ipc.on(EventMessage, (data) => {
  /* ... */
});
ipc.once(EventConnected, (state) => {
  /* ... */
});

ConnectionOptions

Option Type Default Description
transportTypes TransportType | TransportType[] All three, in order Transport(s) to try, left to right
sharedWorkerUri string GitHub CDN fallback URL URL to ipc-worker.js (SharedWorker transport)
storageKey string 'ipc' Namespace prefix for SessionStorage keys
storageExpiredTime number 30000 Message TTL in milliseconds (SessionStorage)

ConnectionState

Returned by connect() and disconnect(), and passed to event callbacks.

Field Type Description
type TransportType | null Active transport, or null if none connected
connected boolean Whether the connection is currently active
error? unknown Error detail when a connection attempt fails

TransportType

import {TransportType} from '@lopatnov/browser-tab-ipc';

TransportType.broadcastChannel; // BroadcastChannel API
TransportType.sharedWorker; // SharedWorker
TransportType.sessionStorage; // SessionStorage events

Events

Constant When emitted
EventConnected A transport connected successfully
EventConnectionError A transport failed to connect
EventDisconnected The connection was closed
EventMessage A message arrived from another tab

SharedWorker Setup

The SharedWorker transport requires a worker script served from your own origin to avoid CORS issues. Copy the bundled file into your project:

cp node_modules/@lopatnov/browser-tab-ipc/dist/ipc-worker.js public/

Then point connect() to it:

await ipc.connect({sharedWorkerUri: '/ipc-worker.js'});

Without this step, the library falls back to a GitHub-hosted worker — which only works on the same origin as the CDN.


Troubleshooting

Module '"events"' can only be default-imported using the 'allowSyntheticDefaultImports' flag

Add to tsconfig.json:

{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true
  }
}

Messages are not received in other tabs

  • All tabs must be on the same origin (protocol + host + port).
  • BroadcastChannel and SessionStorage are strictly same-origin.
  • SharedWorker can bridge origins if the worker file is served from the target origin.

SharedWorker fails silently

  • Open DevTools → Application → Shared Workers and check for errors.
  • Verify the sharedWorkerUri path is accessible from the browser (check for 404).
  • If the file is missing, copy ipc-worker.js to your public/ folder as shown above.

Connection established but no messages arrive

  • Both tabs must call connect() before any messages are sent.
  • A tab does not receive its own messages — only other tabs do.

Contributing

Contributions are welcome! Please read CONTRIBUTING.md before opening a pull request.


Built With


License

Apache-2.0 © 2019–2026 Oleksandr Lopatnov · LinkedIn

About

Lightweight cross-tab messaging library for TypeScript / JavaScript. Exchange messages between browser tabs with automatic transport selection and graceful fallback.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors