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

@utilityjs/use-pub-sub

v2.0.0

Published

A React hook that provides the Publish/Subscribe pattern.

Readme

UtilityJS | usePubSub

A React hook that provides the Publish/Subscribe pattern.

Features

  • Decoupled Communication: Enable components to communicate without direct references
  • Global and Scoped: Support for both global and scoped pub-sub systems
  • React Integration: Seamless integration with React lifecycle and hooks
  • Type-Safe: Full TypeScript support with proper type definitions
  • Lightweight: Minimal overhead with efficient implementation
  • Automatic Cleanup: Automatic subscription cleanup on component unmount

Installation

npm install @utilityjs/use-pub-sub

or

pnpm add @utilityjs/use-pub-sub

Usage

Basic Usage

import { createPubSub } from "@utilityjs/use-pub-sub";

// Create a pub-sub system
const { useSubscribe, publish } = createPubSub();

// Publisher component
function Publisher() {
  const handleClick = () => {
    publish("user-action");
  };

  return <button onClick={handleClick}>Trigger Action</button>;
}

// Subscriber component
function Subscriber() {
  useSubscribe("user-action", () => {
    console.log("User action triggered!");
  });

  return <div>Listening for user actions...</div>;
}

Multiple Subscribers

function App() {
  const { useSubscribe, publish } = createPubSub();

  return (
    <div>
      <Publisher publish={publish} />
      <Subscriber1 useSubscribe={useSubscribe} />
      <Subscriber2 useSubscribe={useSubscribe} />
      <Subscriber3 useSubscribe={useSubscribe} />
    </div>
  );
}

function Subscriber1({ useSubscribe }) {
  useSubscribe("notification", () => {
    console.log("Subscriber 1 received notification");
  });

  return <div>Subscriber 1</div>;
}

function Subscriber2({ useSubscribe }) {
  useSubscribe("notification", () => {
    console.log("Subscriber 2 received notification");
  });

  return <div>Subscriber 2</div>;
}

Scoped Pub-Sub System

// Create a scoped registry for isolation
const modalRegistry = {};
const { useSubscribe, publish } = createPubSub(modalRegistry);

function Modal() {
  useSubscribe("close-modal", () => {
    setIsOpen(false);
  });

  useSubscribe("open-modal", () => {
    setIsOpen(true);
  });

  return isOpen ? <div>Modal Content</div> : null;
}

function ModalControls() {
  return (
    <div>
      <button onClick={() => publish("open-modal")}>Open</button>
      <button onClick={() => publish("close-modal")}>Close</button>
    </div>
  );
}

State Management Pattern

const { useSubscribe, publish } = createPubSub();

// Global state updates
function useGlobalCounter() {
  const [count, setCount] = useState(0);

  useSubscribe("increment", () => setCount(c => c + 1));
  useSubscribe("decrement", () => setCount(c => c - 1));
  useSubscribe("reset", () => setCount(0));

  return count;
}

// Components can trigger state changes
function CounterControls() {
  return (
    <div>
      <button onClick={() => publish("increment")}>+</button>
      <button onClick={() => publish("decrement")}>-</button>
      <button onClick={() => publish("reset")}>Reset</button>
    </div>
  );
}

function CounterDisplay() {
  const count = useGlobalCounter();
  return <div>Count: {count}</div>;
}

Manual Unsubscribe

const { useSubscribe, publish, unsubscribe } = createPubSub();

function Component() {
  const callback = useCallback(() => {
    console.log("Event received");
  }, []);

  useEffect(() => {
    // Manual subscription management
    const unsub = makeSubscribe(registry)("my-channel", callback);

    return () => {
      // Manual cleanup if needed
      unsubscribe("my-channel", callback);
    };
  }, [callback, unsubscribe]);

  return <div>Component</div>;
}

Event System

const { useSubscribe, publish } = createPubSub();

// Event emitter pattern
function useEventSystem() {
  const emit = (event: string) => publish(event);

  return { emit };
}

function GameComponent() {
  const { emit } = useEventSystem();

  useSubscribe("player-died", () => {
    console.log("Game over!");
  });

  useSubscribe("level-completed", () => {
    console.log("Level completed!");
  });

  const handlePlayerDeath = () => emit("player-died");
  const handleLevelComplete = () => emit("level-completed");

  return (
    <div>
      <button onClick={handlePlayerDeath}>Player Dies</button>
      <button onClick={handleLevelComplete}>Complete Level</button>
    </div>
  );
}

API

createPubSub(scopedRegistry?)

Creates a pub-sub system with subscribe hook, publish, and unsubscribe functions.

Parameters

  • scopedRegistry?: Registry - Optional registry for scoped pub-sub system

Returns

Object containing:

  • useSubscribe: Subscribe - React hook for subscribing to channels
  • publish: Publish - Function to publish to channels
  • unsubscribe: Unsubscribe - Function to manually unsubscribe

useSubscribe(channel, callback)

React hook that subscribes to a channel and automatically cleans up on unmount.

Parameters

  • channel: string - The channel name to subscribe to
  • callback: () => void - Function to execute when the channel is published to

publish(channel)

Publishes to a channel, executing all subscribed callbacks.

Parameters

  • channel: string - The channel name to publish to

unsubscribe(channel, callback)

Manually unsubscribes a callback from a channel.

Parameters

  • channel: string - The channel name to unsubscribe from
  • callback: () => void - The callback function to remove

Types

type Callback = () => void;
type Registry = Record<string, Callback[]>;
type Subscribe = (channel: string, callback: Callback) => void;
type Publish = (channel: string) => void;
type Unsubscribe = (channel: string, callback: Callback) => void;

Use Cases

  • Component Communication: Enable distant components to communicate
  • Event Systems: Implement game events, UI events, or custom event systems
  • State Management: Create simple global state management solutions
  • Modal Management: Coordinate modal opening/closing across the app
  • Notification Systems: Broadcast notifications to multiple subscribers
  • Theme Changes: Notify components about theme or configuration changes

Contributing

Read the contributing guide to learn about our development process, how to propose bug fixes and improvements, and how to build and test your changes.

License

This project is licensed under the terms of the MIT license.