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

use-mqtt-hooks

v0.2.0

Published

React hooks for MQTT: a connection provider, reference-counted subscriptions, and retained-message state

Readme

use-mqtt-hooks

CI

Hooks for interfacing with MQTT.js using React idioms:

  • Shared connection management as a React context provider
  • Subscriptions that match component lifecycle and can be multiplexed across components
  • Helpers for aggregating retained messages across a prefix

Install

npm install use-mqtt-hooks

The subscription machinery uses useEffectEvent, so React 19.2 or newer is required. Browsers can't speak the native MQTT protocol, so the broker also needs websocket support enabled. (For Mosquitto, that's a listener block with protocol websockets)

Quick start

Add MqttProvider in your component tree above anything that talks to MQTT. That connects to the MQTT broker and exposes the connection via React context:

import { MqttProvider } from "use-mqtt-hooks";

createRoot(document.getElementById("root")!).render(
  <MqttProvider mqttUrl="ws://localhost:8080" clientPrefix="dashboard">
    <App />
  </MqttProvider>,
);

Below that, subscribe and publish from any component.

import { useMqtt, useMqttListener } from "use-mqtt-hooks";

function Thermostat() {
  const { status, publish } = useMqtt();
  const [temperature, setTemperature] = useState<string>();

  useMqttListener("home/thermostat/temperature", (topic, message) => {
    setTemperature(message.toString());
  });

  return (
    <>
      <p>{status === "connected" ? (temperature ?? "--") : "offline"}</p>
      <button onClick={() => publish?.("home/thermostat/setpoint", "21")}>
        Set to 21
      </button>
    </>
  );
}

API

<MqttProvider>

| Prop | Type | Notes | | -------------- | ------------------------------------------ | -------------------------------------------------------- | | mqttUrl | string \| undefined | Pass undefined to disconnect | | clientPrefix | string | Used to generate a random Client ID (computed per-mount) | | will | { topic, payload, retain? } \| undefined | Last Will and Testament, published at QoS 1 | | keepalive | number \| undefined | Keep-alive interval in seconds; MQTT.js defaults to 60 |

mqttUrl can be unpopulated if either the broker address is not available immediately at mount, or if you want to trigger a disconnection. All hooks degrade quietly when not connected.

The MQTT protocol only allows specifying the will and keepalive at connection time, so will and keepalive are only evaluated when the connection opens; changing either prop while the connection is open won't take effect unless the connection bounces. The client always disconnects un-gracefully, so both clean and unclean shutdowns will publish the will.

MQTT.js closes the connection if the broker hasn't responded within 1.5x the keepalive, so a lower value means a vanished broker gets noticed sooner (at the cost of more ping traffic).

Connections are MQTT v5 with RAP (Retain As Published) enabled, and everything the provider sends uses QoS 1.

useMqtt()

The basic hook for accessing MQTT. Returns an object containing:

  • status: "disconnected", "connecting", or "connected"
  • publish(topic, message, options?): publishes at QoS 1, returns a Promise which resolves once the broker acknowledges the message. Pass { retain: true } to set the retain flag. publish is undefined until the client is created (but will still be present while reconnecting).
  • client: the underlying MQTT.js client, in case you need to do something this library doesn't support.

useMqttListener(topic, callback)

Subscribes to a topic and calls callback(topic, message) as messages arrive. The topic may contain MQTT wildcards (+ or #), and may be an array to subscribe to several at once.

Pass undefined to disable the subscription. (This allows for conditionally subscribing to a topic without violating the rules of hooks.)

The callback does not need to be memoized. It is wrapped in useEffectEvent, so it always sees the latest props and state without churning the subscription.

A message with an empty payload means the retained value on that topic was cleared.

useMqttKv(pattern, transform?)

Projects a wildcard subscription into a ReadonlyMap. The pattern must contain exactly one named wildcard, +key (enforced in the type declaration):

// Map<string, string> keyed by device ID
const devices = useMqttKv("devices/+key/status");

// Map<string, number>
const uptimes = useMqttKv("devices/+key/uptime", Number);

Keys are the observed values of +key and values are the payloads published there, run through transform if you supply one. Clearing a retained topic removes its key from the map. As with useMqttListener, undefined disables the subscription.

useMqttSet(pattern)

The same thing when only the keys matter, returning a ReadonlySet<string> of the +key values seen.

How it works

Most of the effort in this library is in MqttSubTracker, which exists to (a) bridge the semantics of MQTT subscriptions and React component lifecycle semantics and (b) allow multiple components to subscribe to the same topic. That class manages:

Reference counting. Brokers don't reference count subscriptions from a single client, so if two different components subscribed naively, the subscription would get torn down when the first one unsubscribed. Instead, we count subscriber components to each topic pattern, subscribe once, and dispatch incoming messages to every callback whose pattern matches (wildcards included).

Retained message replay. Normally when a client subscribes to a topic with retained messages, those messages get delivered immediately. But since the client may already be subscribed to that topic, that wouldn't naively happen. We track all retained messages (using MQTT v5's RAP flag) and re-deliver them when a new component subscribes to a topic that we already have referenced.

Deferred unsubscription. When a component re-renders, its old cleanup logic runs before the new setup logic. Naively, that would cause us to unsubscribe and resubscribe to topics on every render, creating churn and the potential for missing messages. Instead, unsubscribing is deferred by a tick so that the new subscription has time to establish its reference.

Reconnect repair. When reconnecting, there is some subtlety in handling retained messages correctly without transiently dropping state.

Collectively, these make it possible to use MQTT in a manner consistent with React's hook idioms.

Development

npm run lint    # tsc, eslint, and prettier in parallel
npm test        # vitest
npm run build   # emit dist/

Run npm run lint rather than the individual lint:* scripts.

The hook tests run against a real Mosquitto, which the suite starts on an ephemeral port and shuts down afterwards. Install it (brew install mosquitto or apt-get install mosquitto), or point MOSQUITTO_BIN at a binary somewhere else.

Acknowledgments

Development of this library was sponsored by Strange Bird Immersive, who also agreed to release it as open source. Thanks to them for both.

License

MIT