Skip to content

Getting Started

react-broadcast-sync syncs UI state and user events between browser tabs of the same origin in React apps. It wraps the native BroadcastChannel API in a hook and an optional provider.

This guide walks you from an empty file to two tabs staying in sync, step by step.

Terminal window
npm install react-broadcast-sync

The package declares React and React DOM >=16.8.0 as peer dependencies. The package ships ESM, CJS, and TypeScript types.

There are two ways to open a channel. Pick one per app (or per part of an app):

  • The hook - useBroadcastChannel(name, options) opens a channel inside one component. Call it once per channel. Best for most apps.
  • The provider - <BroadcastProvider> opens one channel at the top of a subtree and shares it through context with useBroadcastProvider(). Best when many components share one channel.

Both return the same actions (messages, postMessage, ping, and friends). The steps below use the hook; the provider version is covered in Use the provider.

import { useBroadcastChannel } from 'react-broadcast-sync';
function Counter() {
const counter = useBroadcastChannel('counter');
// ...
}

Every tab that opens a channel with the same name hears the others. A tab never receives its own messages.

postMessage(type, content) sends to every other tab with the channel open. type is a label you choose; content is anything serializable.

counter.postMessage('increment', { by: 1 });

Incoming messages land in messages, an event log of BroadcastMessage objects. For a counter, keep the count as local state and handle each incoming increment with onMessage. Apply your own click locally too, because the sender never receives its own message:

import { useState } from 'react';
import { useBroadcastChannel } from 'react-broadcast-sync';
export default function Counter() {
const [count, setCount] = useState(0);
const { postMessage } = useBroadcastChannel('counter', {
registeredTypes: ['increment'],
onMessage: {
increment: () => setCount(current => current + 1),
},
});
const increment = () => {
setCount(current => current + 1);
postMessage('increment', { by: 1 });
};
return <button onClick={increment}>Count: {count}</button>;
}

Two things to keep in mind:

  • messages is a log, not current state. keepLatestMessage: true keeps only the newest received message; it does not accumulate increments or replay history.
  • A tab that opens late does not receive messages from before it opened.

Open the page in a second tab (same browser, same origin) and click the button. While both tabs are listening, both count every click: the clicked tab updates locally and the other handles the incoming increment. A new or refreshed tab starts at 0; this example does not restore earlier clicks.