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

browser-tab-elect

v1.0.3

Published

Tab-elect solves the problem of only wanting one browser tab to run a job, and ensure that there is always one browser tab running it even if the previous running tab was closed. This package takes care of the inter-tab communication required to elect one

Readme

browser-tab-elect

Elect a single browser tab as the leader using BroadcastChannel + localStorage fencing. Only the leader performs critical work (e.g., triggering downloads), with automatic failover if the leader tab closes or crashes.

  • Headless core API for any framework
  • React hook useSingleTabLeader
  • Dual ESM/CJS builds with TypeScript types

Install

npm i browser-tab-elect

Core API

import { createLeaderElector } from 'browser-tab-elect';

const elector = createLeaderElector();

elector.start();

if (elector.isLeader()) {
  // perform leader-only action
}

const unsubscribe = elector.subscribe((state) => {
  console.log('leader?', state.isLeader, 'tabId', state.tabId, 'epoch', state.epoch);
});

// later
unsubscribe();
elector.stop();

Notes

  • start() is idempotent. Calling it multiple times is safe and will not attach duplicate listeners or timers.
  • stop() fully cleans up timers, storage listeners, and channel listeners; you can safely call start() again later.
  • If the leader record in localStorage is removed, remaining tabs will automatically run a fresh election.

Options

createLeaderElector({
  storageKey: 'citadel:leader',          // localStorage key
  channelName: 'citadel_leadership',     // BroadcastChannel name
  leaseMs: 8000,                       // leader lease duration
  renewEveryMs: 3000,                  // heartbeat interval
  electionMinBackoffMs: 80,            // election backoff window
  electionMaxBackoffMs: 200,
});

React Hook

import { useSingleTabLeader } from 'browser-tab-elect/react';

function Component() {
  const { isLeader } = useSingleTabLeader();

  // gate work
  useEffect(() => {
    if (!isLeader) return;
    // leader-only logic
  }, [isLeader]);

  return <div>{isLeader ? 'Leader' : 'Follower'}</div>;
}

Options and identity

useSingleTabLeader(options) reuses (or creates) an elector instance keyed by the full option set. Changing any of these will create and start a new elector instance:

  • storageKey, channelName
  • leaseMs, renewEveryMs
  • electionMinBackoffMs, electionMaxBackoffMs

This ensures the hook reflects configuration changes deterministically without leaking prior listeners.

Protocol (high-level)

  • Each tab has a UUID and competes to lead when there's no valid leader.
  • Leader writes {tabId, epoch, leaseUntil} to localStorage and renews leaseUntil every ~3s.
  • Followers accept the freshest epoch with a live lease and wait.
  • If the lease expires or the leader steps down, a new election runs. Winner is deterministic: lexicographically highest tabId among candidates.
  • Fencing: leader actions must verify the record matches their tabId and has a live lease.

SSR and Fallbacks

  • APIs are safe to import server-side; they no-op when window is not available.
  • If BroadcastChannel is missing, election still functions via storage reads/writes (less responsive).

Exports

  • Root: core API
  • Subpath react: React hook
{
  "exports": {
    ".": { "import": "./dist/index.mjs", "require": "./dist/index.js" },
    "./react": { "import": "./dist/react/index.mjs", "require": "./dist/react/index.js" }
  }
}

License

ISC