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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@loanpal/react-websocket

v0.1.2

Published

React webSocket client

Maintainers

che-lorche-lorbfieberbfiebermcrisanmcrisanmariana-dumitriumariana-dumitriujgonzalez-gljgonzalez-glpetrubrinzapetrubrinzaarie.whitearie.whitesspatarisspatarimboyd33mboyd33jduff200jduff200stevegarciastevegarciaepirleaepirlealpopalpopagabrielsasgabrielsashsanchez67hsanchez67richardjhallrichardjhallngonzalez625ngonzalez625alexventura11alexventura11gcornejogcornejojroque-loanpaljroque-loanpalioana.pascaioana.pascajfackrelljfackrellhgonzalezhgonzalezadriancontantinacheadriancontantinachegkang-goodleapgkang-goodleapskinserskinserrbadiurbadiuipoenaruipoenaruiakabpauliakabpaullemonsqueezelemonsqueezescharpentierscharpentierecarvajal97ecarvajal97fneira.goodleapfneira.goodleapshmigshmigdcearnaudcearnauafrancuafrancuahewitt_goodleapahewitt_goodleapvanaathirayanvanaathirayanalevensteinalevensteinsorelmitra.goodleapsorelmitra.goodleapzadokkimzadokkimsimsidney2simsidney2natejswenson-goodleapnatejswenson-goodleapmpratt28mpratt28tapetapetapetapeglevolosciucglevolosciucradunraduneliodorpopoveliodorpopovvladutilincavladutilincagoodleapcarlosgoodleapcarlosvglavanvglavandanielfratilagldanielfratilaglandreigorgoiandreigorgoigabriela.macoveigabriela.macoveimmahadevaiahmmahadevaiahlgonzalezgllgonzalezglvalerii-tudosevalerii-tudoseachetruscaachetruscajcsquaredxcjcsquaredxcafurbushafurbushalex-serban-techqalex-serban-techqdcrisandcrisandschatzledschatzlecprobstglcprobstglivangoodleapivangoodleapssmall-glssmall-glicusniricusnirrpeterson66rpeterson66dnevarezdnevarezconellisconellisamj-glamj-glhsingh_glhsingh_glcamiamcamiamisoriaisoriaabetz-goodleapabetz-goodleapsunitaprakash1sunitaprakash1atoppoatoppohzafar-at-goodleaphzafar-at-goodleapimotelicaimotelicajoeftljoeftlmjoshiglmjoshigladrianagoreaadrianagoreaalex.ivialex.iviandreicgoodleapandreicgoodleapjquilliganjquilliganskavuruskavurusgordonsgordonandreispacandreispacbahnenbahnenmirceaadasmirceaadasteodorapetricteodorapetricsanand-goodleapsanand-goodleapmatthewo-glmatthewo-glsmiraclesmiraclecliffjohnson-glcliffjohnson-glrgurnani-glrgurnani-glknicolknicolquinnland23quinnland23dpalmer63dpalmer63vgageavgagearaluca.arghiroiuraluca.arghiroiugtaggartgtaggarttkovacstkovacsbhustonbhustonvladimir.tarasencovladimir.tarasencossubramanianglssubramanianglaghosh-goodleapaghosh-goodleapgl-tonygl-tonydevturpgldevturpglrolland-schnellrolland-schnellgoodleap-service-usergoodleap-service-userskaur1skaur1alicia-villanuevaalicia-villanuevarfernandez1rfernandez1pypppyppmhenleymhenleyvictorgoodleapvictorgoodleaprazvanloanpalrazvanloanpaljzealjzealgoodleapgiorgiogoodleapgiorgiodhelbergdhelbergcdarrigocdarrigormoldovanrmoldovantudoralexuctudoralexucdvillagradvillagrabudigabebudigabeaapostol.gdlpaapostol.gdlpchenry216chenry216bogdanpopglbogdanpopglracostaglracostaglcristian.sanducristian.sandukheldkheldmheindel.goodleapmheindel.goodleapmsharma101msharma101jypsyjill-gljypsyjill-glwhayward-glwhayward-glijonesijonespaulina_depaulina_deanmol.closeloopanmol.closeloopdevendra_27devendra_27sonu-verma-clsonu-verma-clgwadley-goodleapgwadley-goodleapdrolegdrolegjpiersigillijpiersigillisterentiisterentiiaiordanaiordanjohn-goodleapjohn-goodleappmarinucpmarinuccthornton-goodleapcthornton-goodleapericfitzgeraldgoodleapericfitzgeraldgoodleaprchirigutrchirigutraddueradduemgeorgescumgeorgescujordanschwartzgljordanschwartzglvmicuvmiculdleimaldleimaaseetharamanaseetharamanawatve_goodleapawatve_goodleapskabbes-glskabbes-gllyarreddulyarreddusherwin-goodleapsherwin-goodleapaloaiza-goodleapaloaiza-goodleapgabi38glgabi38glajnicholsajnicholsdestryrusselldestryrussellhope_pratthope_prattdgrozavudgrozavujcinquegranajcinquegranaandersperryglandersperryglkrasniqib1krasniqib1dricegoodleapdricegoodleapaniabrahamaniabrahamalka_singhalka_singhceasterbrooksceasterbrooksbforangoodleapbforangoodleapkpatel_goodleapkpatel_goodleapdadeniyigldadeniyiglgmena-goodleapgmena-goodleapsnegarasnegara

Keywords

Readme

About

This is a hook-based React websocket implementation.

Installation

From your react project folder, run: pnpm add @loanpal/react-websocket

WebSocket Implementation

This directory contains the WebSocket implementation using Zustand for state management.

Components

websocket-client.ts

The core WebSocket client class that handles:

  • Connection management
  • Automatic reconnection (max 3 retries)
  • Message handling and delivery to all listeners
  • Subscription management

Key Features:

  • Only one connection is created per URL
  • Automatic reconnection on connection loss
  • Type-safe message handling
  • Simple single-channel design

websocket-store.ts

Zustand store that manages:

  • Multiple WebSocket clients by URL
  • Client lifecycle (creation, cleanup)
  • Global state access

use-websocket.tsx

React hook for subscribing to WebSocket messages:

useWebsocket({
  componentName: "LeadDetailsPage",
  url: "ws://localhost:8000/api/v1/leads/019a0c08-3364-7afe-9bec-9ce9c5709b4e/ws",
  connectionId: "lead-updates", // Unique ID for this connection
  protocols: `access_token.${getAccessToken()}`, // Optional: Pass auth via subprotocol
  messageFilter: (msg) => msg.subject === "lead-123", // Optional: Filter messages
  auth: {
    // Option 1: Plain object sent as first message after connection
    forwardedProps: {
      app_id: "HOMEOWNER",
      access_token: getAccessToken(),
    },
    // ... other required fields
  },
  // Or Option 2: Function for custom auth logic
  // auth: (send) => {
  //   send({
  //     forwardedProps: {
  //       app_id: "HOMEOWNER",
  //       access_token: getAccessToken(),
  //     },
  //   });
  // },
  onMessage: (message) => {
    console.log("Received:", message);
  },
});

Features:

  • Multiple connections per URL: Use different connectionId values to create separate physical connections to the same URL
  • Message filtering: Optional messageFilter callback to selectively receive messages
  • Internally memoizes callbacks (no need for useCallback)
  • Auto-cleanup on unmount
  • Optional protocols parameter for passing auth tokens or headers via WebSocket subprotocol
  • Optional auth parameter for sending authentication on connection (supports both plain object and function)

websocket-debug-panel.tsx

Alt text description

Development-only UI component that displays:

  • Active WebSocket connections
  • Connection status (CONNECTING, OPEN, CLOSING, CLOSED)
  • Number of listeners per connection
  • Real-time updates every second

Usage:

import { WebsocketDebugPanel } from "@loanpal/react-websocket";

// inside your applicaiton code, simply render the component
return (
  <WebsocketDebugPanel />
)

Usage Example

import { useWebsocket } from "@loanpal/react-websocket";
import { getAccessToken } from "@/shared/utils/cookie-handler";

function MyComponent() {
  const { send, isConnected } = useWebsocket({
    componentName: "MyComponent",
    url: "ws://localhost:8000/api/v1/ws",
    connectionId: "my-connection", // Required: unique ID for this connection
    // Option 1: Pass auth via subprotocol header
    protocols: `access_token.${getAccessToken()}`,
    // Option 2: Send auth as plain object (first message after connection)
    auth: {
      forwardedProps: {
        app_id: "HOMEOWNER",
        access_token: getAccessToken(),
      },
      // ... other required fields from AGUIUserFirstMessageType
    },
    // Option 3: Or use function for custom auth logic
    // auth: (send) => {
    //   send({
    //     forwardedProps: {
    //       app_id: "HOMEOWNER",
    //       access_token: getAccessToken(),
    //     },
    //   });
    // },
    onMessage: (message) => {
      // Handle all messages
      console.log("Received message:", message);
    },
  });

  // Send messages using the send function
  const sendCustomMessage = () => {
    send({ type: "custom", data: "hello" });
  };

  return (
    <div>
      My Component - {isConnected ? "Connected" : "Disconnected"}
    </div>
  );
}

Message Filtering

The messageFilter parameter allows you to selectively receive messages based on custom criteria. This is particularly useful when the server sends messages with identifying fields (like subject, channel, or topic) and you only want to handle specific messages:

type SubjectMessage = {
  subject: string;
  data: any;
};

function UserProfile({ userId }: { userId: string }) {
  // Only receive messages for this specific user
  const { send, isConnected } = useWebsocket<SubjectMessage>({
    url: "ws://api/updates",
    connectionId: "user-updates",
    messageFilter: (msg) => msg.subject === userId,
    onMessage: (message) => {
      // This will only be called for messages matching the userId
      console.log("Update for user:", message.data);
    },
  });

  const sendUpdate = () => {
    send({ subject: userId, data: { status: "active" } });
  };

  return <div>User Profile - {isConnected ? "Online" : "Offline"}</div>;
}

Key Points:

  • The filter is applied before onMessage is called
  • Filtered-out messages are silently ignored (onMessage won't be called)
  • The filter function receives the fully typed message
  • If messageFilter is not provided, all messages are passed to onMessage

Common Use Cases:

// Filter by subject/channel
messageFilter: (msg) => msg.subject === "user-123"

// Filter by message type
messageFilter: (msg) => msg.type === "notification"

// Filter by multiple criteria
messageFilter: (msg) =>
  msg.channel === "updates" && msg.priority === "high"

// Complex filtering logic
messageFilter: (msg) => {
  if (msg.type === "alert") return true;
  if (msg.type === "update" && msg.userId === currentUserId) return true;
  return false;
}

Connection Identity & Multiple Connections

The websocket implementation uses a composite key of url:connectionId to identify unique connections:

// These create TWO separate physical WebSocket connections
const { send: send1 } = useWebsocket({
  url: "ws://example.com/api",
  connectionId: "session-1",  // Connection 1
  onMessage: (msg) => console.log("Session 1:", msg)
});

const { send: send2 } = useWebsocket({
  url: "ws://example.com/api",
  connectionId: "session-2",  // Connection 2 (different physical connection)
  onMessage: (msg) => console.log("Session 2:", msg)
});

// These share the SAME physical WebSocket connection
const { send: send3 } = useWebsocket({
  url: "ws://example.com/api",
  connectionId: "session-1",  // Reuses Connection 1
  onMessage: (msg) => console.log("Also Session 1:", msg)
});

Key behaviors:

  • Same url + same connectionId = shared connection (all listeners receive all messages)
  • Same url + different connectionId = separate connections (each receives different server data)
  • Requires backend support for multiple concurrent connections per user

Generic Type Support

The useWebsocket hook requires you to specify your message type for full type safety:

// Define your message types
type ChatMessage = {
  type: 'chat';
  user: string;
  text: string;
  timestamp: number;
};

type NotificationMessage = {
  type: 'notification';
  title: string;
  body: string;
  priority: 'high' | 'low';
};

// Use with specific message type (type parameter is required)
const { send: sendChat, isConnected: chatConnected } = useWebsocket<ChatMessage>({
  url: "ws://api/chat",
  connectionId: "chat-room-1",
  onMessage: (msg) => {
    // msg is fully typed as ChatMessage!
    console.log(`${msg.user}: ${msg.text}`);
  }
});

// Send a chat message
sendChat({ type: 'chat', user: 'Alice', text: 'Hello!', timestamp: Date.now() });

// Different connection with different message type
const { send: sendNotif, isConnected: notifConnected } = useWebsocket<NotificationMessage>({
  url: "ws://api/notifications",
  connectionId: "user-notifications",
  onMessage: (msg) => {
    // msg is fully typed as NotificationMessage!
    if (msg.priority === 'high') {
      alert(msg.title);
    }
  }
});

Creating Typed Wrapper Hooks

For cleaner APIs, create client-specific wrapper hooks:

// hooks/use-chat-websocket.ts
export const useChatWebsocket = (roomId: string, onMessage: (msg: ChatMessage) => void) => {
  return useWebsocket<ChatMessage>({
    url: `ws://api/chat/${roomId}`,
    connectionId: `chat-${roomId}`,
    onMessage,
    componentName: 'ChatClient',
  });
};

// Usage - much cleaner!
const { send, isConnected } = useChatWebsocket('room-123', (msg) => {
  console.log(msg.user, msg.text); // Fully typed!
});

// Send a message
if (isConnected) {
  send({ type: 'chat', user: 'Bob', text: 'Hi!', timestamp: Date.now() });
}

Architecture

┌─────────────────────┐
│  useWebsocket       │
│      (Hook)         │
└──────────┬──────────┘
           │
           ▼
┌─────────────────────┐
│  WebsocketStore     │
│     (Zustand)       │
│  Key: url:connId    │
└──────────┬──────────┘
           │
           ▼
┌─────────────────────┐
│  WebsocketClient    │
│      (Class)        │
└──────────┬──────────┘
           │
           ▼
┌─────────────────────┐
│   Native WebSocket  |
│      (Class)        │
└─────────────────────┘

Key Benefits

  1. Multiple Connections per URL: Create separate connections using different connectionId values
  2. Shared Connections: Components with the same url:connectionId share a single connection
  3. Generic Type Support: Fully typed messages using TypeScript generics for different message schemas
  4. No Provider Needed: No need to wrap components in a provider
  5. Auto-memoization: Callbacks are internally memoized
  6. Type-safe: Full TypeScript support with compile-time type checking
  7. Debug UI: Easy debugging with the debug panel