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

@maxtroost/use-websocket

v1.1.9

Published

A robust WebSocket connection management package for React applications with automatic reconnection, heartbeat monitoring, URI-based message routing, and React integration via TanStack Store.

Readme

@maxtroost/use-websocket

A robust WebSocket connection management package for React applications with automatic reconnection, heartbeat monitoring, URI-based message routing, and React integration via TanStack Store.

Installation

npm install @maxtroost/use-websocket

Peer dependencies: React 18+, React DOM 18+

Message Format

All outgoing WebSocket messages share the same structure and are sent as JSON:

{
  "method": "subscribe" | "unsubscribe" | "post",
  "uri": "/path/to/endpoint",
  "body": { ... }
}

| Field | Required | Description | | ------- | -------- | --------------------------------------------------------------------------- | | method| Optional | HTTP-like method: subscribe, unsubscribe, or post (default for custom messages) | | uri | Yes | Path for routing; the server uses this to dispatch to the correct handler | | body | Optional | Payload sent with the message |

Examples:

  • Subscribe (streaming): { "method": "subscribe", "uri": "/notifications", "body": { "status": "active" } }
  • Unsubscribe: { "method": "unsubscribe", "uri": "/notifications" }
  • Request/response (e.g. validate, mark read): { "method": "post", "uri": "/voyages/modify/validate", "body": { ... } }

You can add extra fields (e.g. auth headers) via transformMessagePayload in WebsocketClient.

Subscription vs Message

The package offers two patterns for different use cases:

| | Subscription (useWebsocketSubscription) | Message (useWebsocketMessage) | | --- | --- | --- | | Pattern | Streaming — subscribe once, receive ongoing updates | Request/response — send a message, get one reply (or none) | | Use case | Live data feeds (notifications, voyage list, real-time dashboards) | One-off commands (validate, modify, mark read) | | URI | Fixed per hook — one URI per subscription | Any URI — send to different endpoints per call | | State | TanStack Store — reactive message, pendingSubscription, connected | No store — returns a Promise or fire-and-forget | | Lifecycle | Auto-subscribes when connection opens; unsubscribes when last component unmounts | No subscription — just send when needed |


Basic Setup

  1. Create a WebsocketClient and wrap your app with WebsocketClientProvider:
import { WebsocketClient, WebsocketClientProvider } from "@maxtroost/use-websocket";

const websocketClient = new WebsocketClient({
  maxRetryAttempts: 20,
  // Optional: customize heartbeat, timeouts, logging, etc.
});

function App() {
  return (
    <WebsocketClientProvider client={websocketClient}>
      <YourApp />
    </WebsocketClientProvider>
  );
}
  1. Use the hooks in your components:
import { useWebsocketSubscription, useSelector } from "@maxtroost/use-websocket";

function LiveNotifications() {
  const api = useWebsocketSubscription<Notification[]>({
    key: "notifications",
    url: "wss://api.example.com/ws",
    uri: "/notifications",
  });

  const notifications = useSelector(api.store, (s) => s.message);
  const loading = useSelector(api.store, (s) => s.pendingSubscription);

  if (loading) return <div>Connecting...</div>;
  return (
    <ul>
      {notifications?.map((n) => (
        <li key={n.id}>{n.text}</li>
      ))}
    </ul>
  );
}

Features

  • Automatic reconnection — Exponential backoff (4s → 30s → 90s) with configurable max attempts
  • Heartbeat monitoring — Ping/pong to detect stale connections
  • URI-based routing — Messages routed by URI; one connection per URL shared across subscriptions
  • TanStack Store integration — Reactive state for subscriptions; components re-render on updates
  • Two patternsuseWebsocketSubscription for streaming data, useWebsocketMessage for request/response
  • Shared stores — Child components access parent subscriptions via useWebsocketSubscriptionByKey
  • Conditional subscriptionsenabled option to pause when unauthenticated or feature-flagged off
  • Lifecycle callbacksonSubscribe, onMessage, onError, onClose for logging and side effects
  • Connection eventsconnectionEvent callback for reconnection status, logging, or custom notifications

API Reference

| Export | Description | | ------ | ----------- | | useWebsocketSubscription | Subscribe to a URI and receive streaming data via a reactive store | | useWebsocketSubscriptionByKey | Access the store of a subscription created elsewhere (by key) | | useSelector | Select a value from a subscription store with reactive updates | | useWebsocketMessage | Send request/response messages to any URI | | WebsocketClient | Client configuration; instantiate and pass to WebsocketClientProvider; reconnectAllConnections() for manual retry | | WebsocketClientProvider | Context provider; wrap your app to enable hooks | | WebsocketConnection | Low-level connection class; setCustomLogger for debugging | | ReadyState, WebsocketSubscriptionStore, WebsocketTransportError, WebsocketServerError | Types |


Examples

Subscription (Streaming Data)

Subscribe to a URI and receive streaming data via a reactive TanStack Store.

import { useWebsocketSubscription, useSelector } from "@maxtroost/use-websocket";

interface Voyage {
  id: string;
  name: string;
  status: string;
}

function VoyageList() {
  const voyageApi = useWebsocketSubscription<Voyage[], { status: string }>({
    key: "voyages-list",
    url: "wss://api.example.com/ws",
    uri: "/api/voyages",
    body: { status: "active" },
  });

  const voyages = useSelector(voyageApi.store, (s) => s.message);
  const pending = useSelector(voyageApi.store, (s) => s.pendingSubscription);
  const connected = useSelector(voyageApi.store, (s) => s.connected);

  if (pending) return <Skeleton />;
  return (
    <div>
      {!connected && <span>Reconnecting...</span>}
      {voyages?.map((v) => (
        <div key={v.id}>{v.name}</div>
      ))}
    </div>
  );
}

Access Store by Key (Child Components)

When a parent creates the subscription, children can access the same store by key.

import { useWebsocketSubscriptionByKey, useSelector } from "@maxtroost/use-websocket";

function VoyageCount() {
  const voyagesStore = useWebsocketSubscriptionByKey<Voyage[]>("voyages-list");
  const voyages = useSelector(voyagesStore, (s) => s.message);
  return <div>Total: {voyages?.length ?? 0}</div>;
}

Message API (Request/Response)

For one-off commands (validate, modify, mark read) — send a message and optionally await a response.

import { useWebsocketMessage } from "@maxtroost/use-websocket";

function VoyageActions() {
  const api = useWebsocketMessage<ValidationResult, FormValues>({
    key: "voyages/modify",
    url: "wss://api.example.com/ws",
    responseTimeoutMs: 5000,
  });

  const handleValidate = async () => {
    const result = await api.sendMessage(
      "voyages/modify/validate",
      "post",
      formValues
    );
    if (result.valid) {
      // proceed
    }
  };

  const handleMarkRead = () => {
    api.sendMessageNoWait(`notifications/${id}/read`, "post");
  };

  return (
    <>
      <button onClick={handleValidate}>Validate</button>
      <button onClick={handleMarkRead}>Mark Read</button>
    </>
  );
}

Conditional Subscription

Disable the subscription when the user is not authenticated or when a feature flag is off.

function VoyageList({ isAuthenticated }: { isAuthenticated: boolean }) {
  const api = useWebsocketSubscription<Voyage[]>({
    key: "voyages-list",
    url: "wss://api.example.com/ws",
    uri: "/api/voyages",
    enabled: isAuthenticated,
  });
  // ...
}

Advanced Examples

Custom WebsocketClient Configuration

const websocketClient = new WebsocketClient({
  maxRetryAttempts: 10,
  notificationThreshold: 5,
  messageResponseTimeoutMs: 5000,
  heartbeat: { enabled: true, intervalMs: 30000 },
  connectionEvent: (event) => {
    if (event.type === "reconnecting") {
      analytics.track("websocket_reconnecting", { url: event.url });
    }
  },
});

Auth Token in WebSocket URL

When the WebSocket URL includes an auth token, pass the full URL to the hook. When the token changes, the hook automatically calls replaceUrl to reconnect with the new URL.

function VoyageList() {
  const { token } = useAuth();
  const wsUrl = token ? `wss://api.example.com/ws?token=${token}` : null;

  const api = useWebsocketSubscription<Voyage[]>({
    key: "voyages",
    url: wsUrl ?? "", // Hook handles URL changes via replaceUrl
    uri: "/api/voyages",
    enabled: !!token,
  });
  // ...
}

To manually retry after reconnection stops (e.g. user clicks "Retry"): websocketClient.reconnectAllConnections().

Transform Outgoing Messages (e.g. Add Auth Header)

const websocketClient = new WebsocketClient({
  transformMessagePayload: (payload) => ({
    ...payload,
    headers: {
      ...payload.headers,
      Authorization: `Bearer ${getAuthToken()}`,
    },
  }),
});

Lifecycle Callbacks

const api = useWebsocketSubscription<Voyage[]>({
  key: "voyages",
  url: "wss://api.example.com/ws",
  uri: "/api/voyages",
  onSubscribe: ({ uri }) => console.log("Subscribed to", uri),
  onMessage: ({ data }) => console.log("Received", data),
  onError: (error) => {
    if (error.type === "transport")
      console.error("Connection error", error.event);
  },
  onMessageError: (error) => {
    if (error.type === "server")
      console.error("Server error", error.message);
  },
  onClose: (event) => console.log("Connection closed", event.code),
});

Per-Call Timeout Override

const result = await api.sendMessage("/api/command", "post", body, {
  timeout: 3000,
});

Documentation

For contributors and deeper architecture details:

  • WEBSOCKET_CONNECTION.md — Connection lifecycle, class diagrams, URI API lifecycle, browser online/offline handling, full API reference
  • CHART.md — Mermaid flow diagrams for hooks, connection, and error flows

License

MIT