v0.3.0 · MIT

Syncyourtabs in real time

Connect state across tabs instantly. No server, no WebSockets, no extra configuration.

pnpm add tabus-js

925 B

Min + gzip

0

Dependencies

0

Servers

app.my-project.dev
Store3
Checkout
Profile

Shared cart

Tabus Sneakers

Synced stateActive
bus.emit('cart:add')
Tab B received the event

Tab A → B

Event in ~0ms

See it live

shop.tabus.devTab A

Shared cart

Tabus Sneakers

0
Synced stateActive
Waiting for events from the other tab…
shop.tabus.devTab B

Shared cart

Tabus Sneakers

0
Synced stateActive
Waiting for events from the other tab…

Small, but just enough

No heavy runtime, no new concepts. One class, two methods, and the browser does the rest.

01

Native BroadcastChannel

Sync state across tabs with the browser's native API. No WebSockets, no polling, no server in between.

02

In-memory fallback

No BroadcastChannel in your environment? It automatically falls back to an in-memory bus, same API.

03

Typed events

Define your event map with generics. emit and on get real autocomplete and type checking.

04

Built-in throttling

Limit high-frequency events with a single option — ideal for shared scroll or resize handling.

05

Zero dependencies

Nothing to audit, nothing to update. It doesn't drag a dependency tree into your bundle.

06

Tiny

925 B min+gzip, CJS + ESM, and types included. Install it and forget about it.

cart.ts
import { Tabus } from 'tabus-js'

type Events = {
  'cart:add': { id: string }
  'cart:reset': void
}

const bus = new Tabus<Events>('shop')

// listen on the other tabs
bus.on('cart:add', ({ id }) => {
  addToCart(id)
})

// emit from this one
bus.emit('cart:add', { id: 'sku_42' })

// the full API

This is all you write

You create a typed channel, listen on the other tabs, and emit from yours. There's no step four.

  • Create the channel
  • Listen on the other tabs
  • Emit from yours

// works wherever you already do

No framework required

It's a JS class built on a standard API. Wrap it in a hook, a composable, or use it directly.

useTabSync.ts
import { useEffect } from 'react'
import { Tabus } from 'tabus-js'

export const useTabSync = (onAdd: (id: string) => void) => {
  useEffect(() => {
    const bus = new Tabus<{ 'cart:add': { id: string } }>('shop')
    bus.on('cart:add', ({ id }) => onAdd(id))
    return () => bus.destroy()
  }, [])
}

Stop fighting with localStorage.

Sync your tabs in about as long as it takes to read the docs.

~/my-project
$pnpm add tabus-js
Read the docs