npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lopatnov/browser-tab-ipc

v2.1.0

Published

Inter-process communication mechanism between browser tabs. A bus network between browser tabs.

Readme

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