Skip to content

Quick start

This guide walks through the basic usage of tabus-js step by step, until you have two tabs in sync.

Tabus is generic: define a type with the events you’re going to emit and their payloads. This gives you autocomplete and type checking on on() and emit().

type MyEvents = {
data: { value: number };
reset: void;
};

Instantiate Tabus passing a channel name. All instances created with the same name, in any tab of the same origin, communicate with each other.

import { Tabus } from "tabus-js";
const bus = new Tabus<MyEvents>("my-app");

If you don’t pass a name, "tabus" is used by default.

on() accepts both your custom events and the internal lifecycle events tab:join and tab:leave:

bus.on("data", ({ value }) => console.log("value received:", value));
bus.on("tab:join", ({ tabId }) => console.log("new tab connected:", tabId));
bus.on("tab:leave", ({ tabId }) => console.log("tab closed:", tabId));

emit() broadcasts the event to all other instances on the same channel:

bus.emit("data", { value: 42 });

Call destroy() when you no longer need the instance, for example when closing the tab. It emits tab:leave, closes the underlying transport, and clears all handlers:

window.addEventListener("beforeunload", () => bus.destroy());
import { Tabus } from "tabus-js";
type MyEvents = {
data: { value: number };
reset: void;
};
// All instances with the same channel name communicate with each other.
const bus = new Tabus<MyEvents>("my-app");
bus.on("data", ({ value }) => console.log("value received:", value));
bus.on("tab:join", ({ tabId }) => console.log("new tab connected:", tabId));
bus.on("tab:leave", ({ tabId }) => console.log("tab closed:", tabId));
// Emit an event to all OTHER tabs (this tab doesn't receive its own)
bus.emit("data", { value: 42 });
// Clean up when the tab closes
window.addEventListener("beforeunload", () => bus.destroy());

To try it out: open the same page in two tabs, run bus.emit("data", { value: 42 }) in the console of one, and watch the console.log in the other.

  • To understand the reasoning behind these decisions (deduplication, deferred tab:join, idempotent destroy()), check out How it works.
  • If your app uses React, Vue, Angular, or Svelte, check out the Integrations section for each framework’s idiomatic pattern.