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

socket.io-react-hooks-advanced

v1.0.8

Published

A modular and extensible React + Socket.IO hook library designed for real-world applications. Supports namespaced sockets, reconnection strategies, offline queues, latency monitoring, middleware, encryption, and more.

Readme

socket.io-react-hooks-advanced

A modular and extensible React + Socket.IO hook library designed for real-world applications. Supports namespaced sockets, reconnection strategies, offline queues, latency monitoring, middleware, encryption, and more.


✨ Features

  • Auto-Reconnect with exponential backoff, customizable retry strategy
  • Token Injection with support for refresh on unauthorized (401 responses)
  • Latency Monitoring: track round-trip latency in real time (ping-pong events)
  • Offline Background Queue: queue emit calls while offline, flush after reconnect
  • LocalStorage Queue Persistence: retain queue across page reloads, with TTL expiration
  • Middleware for intercepting and transforming outgoing/incoming socket events
  • AES Encryption of payloads (optional, using crypto-js)
  • Namespaced Socket Providers with createNamespacedSocket() for modular isolation
  • Fully Typed Hooks: useSocketContext, useEvent, useLatency, etc.
  • Timeout-based ack() emits: emit events with callback, with timeout fallback behavior
  • Event Subscription Helpers: auto cleanup with useEvent() hook
  • Queue Overflow Handling: detect and manage maximum queue size violations
  • Scoped Event Middleware: different middlewares per namespace
  • Integrated Debug Logging: toggle log output for development

📦 Installation

npm install socket.io-react-hooks-advanced

Also install peer dependencies:

npm install react socket.io-client crypto-js

🧠 Basic Usage


import React, {useEffect, useState} from "react";
import {
    SocketProvider,
    useSocketContext,
    useEvent
} from "socket.io-react-hooks-advanced";

function App() {
    return (
        <SocketProvider
            url="http://localhost:3000"
            getToken={() => localStorage.getItem("token") || ""}
            useEncryption={true}
            encryptionKey="my-secret-key"
            debug={true}
            extraHeaders={{platform: "web", appId: "myAppId123"}} // x-platform, x-appId, other...
        >
            <Main/>
        </SocketProvider>
    );
}

function Main() {
    const {connected, emit, queue, latency} = useSocketContext();
    const [message, setMessage] = useState("");
    const [response, setResponse] = useState<string | null>(null);
    const [received, setReceived] = useState<string[]>([]);

    useEvent("chat:receive", (msg) => {
        setReceived((prev) => [...prev, JSON.stringify(msg)]);
    });

    const sendMessage = () => {
        emit(
            "chat:send",
            {text: message},
            {
                ack: (res) => setResponse(JSON.stringify(res)),
                timeout: 3000,
                encrypt: true
            }
        );
        setMessage("");
    };

    return (
        <div>
            <h2>Socket.IO React Example</h2>
            <p>Status: {connected ? "🟢 Connected" : "🔴 Disconnected"}</p>
            <p>Ping: {latency ?? "..."} ms</p>

            <input
                type="text"
                value={message}
                onChange={(e) => setMessage(e.target.value)}
                placeholder="Type your message"
            />
            <button onClick={sendMessage}>Send</button>

            {response && <pre>Response: {response}</pre>}

            {received.length > 0 && (
                <div>
                    <h4>Received Messages</h4>
                    <ul>
                        {received.map((msg, i) => (
                            <li key={i}>{msg}</li>
                        ))}
                    </ul>
                </div>
            )}

            {queue.length > 0 && (
                <div>
                    <h4>Offline Queue</h4>
                    <ul>
                        {queue.map((q, i) => (
                            <li key={i}>{q.event} - {JSON.stringify(q.data)}</li>
                        ))}
                    </ul>
                </div>
            )}
        </div>
    );
}

🧪 Namespaced Sockets

// chatSocket.ts
import {createNamespacedSocket} from "socket.io-react-hooks-advanced";

export const chatSocket = createNamespacedSocket();

// notifSocket.ts
import {createNamespacedSocket} from "socket.io-react-hooks-advanced";

export const notifSocket = createNamespacedSocket();

// App.tsx
import {chatSocket} from "./chatSocket";
import {notifSocket} from "./notifSocket";

function App() {
    return (
        <notifSocket.Provider url="http://localhost:3000" namespace="/notifications">
            <chatSocket.Provider url="http://localhost:3000" namespace="/chat">
                <Main/>
            </chatSocket.Provider>
        </notifSocket.Provider>
    );
}

// Main.tsx
import React from "react";
import {Chat} from "./Chat";
import {Notifications} from "./Notifications";

export function Main() {
    return (
        <>
            <Chat/>
            <Notifications/>
        </>
    );
}

// Chat.tsx
import React, {useState, useEffect} from "react";
import {chatSocket} from "./chatSocket";

export function Chat() {
    const {emit, connected} = chatSocket.useSocket();
    const [msg, setMsg] = useState("");
    const [log, setLog] = useState<string[]>([]);

    chatSocket.useEvent("chat:message", (data) => {
        setLog((prev) => [...prev, JSON.stringify(data)]);
    });

    return (
        <div>
            <h3>Chat Room</h3>
            <p>Status: {connected ? "🟢" : "🔴"}</p>
            <input
                value={msg}
                onChange={(e) => setMsg(e.target.value)}
                placeholder="Type"
            />
            <button onClick={() => emit("chat:message", {msg})}>Send</button>

            <ul>
                {log.map((l, i) => (
                    <li key={i}>{l}</li>
                ))}
            </ul>
        </div>
    );
}

// Notifications.tsx
import React, {useEffect, useState} from "react";
import {notifSocket} from "./notifSocket";

export function Notifications() {
    const {connected} = notifSocket.useSocket();
    const [alerts, setAlerts] = useState<string[]>([]);

    notifSocket.useEvent("notif:alert", (data) => {
        setAlerts((prev) => [...prev, JSON.stringify(data)]);
    });

    return (
        <div>
            <h3>Notifications</h3>
            <p>Status: {connected ? "🟢" : "🔴"}</p>
            <ul>
                {alerts.map((alert, i) => (
                    <li key={i}>{alert}</li>
                ))}
            </ul>
        </div>
    );
}

You can create and use multiple isolated namespaces simultaneously.


📊 Latency Monitoring

import {useLatency} from "socket.io-react-hooks-advanced";

const PingIndicator = () => {
    const latency = useLatency();
    return <div>Ping: {latency ?? "..."} ms</div>;
};

Latency is measured using ping/pong roundtrip at regular intervals.


🔐 Encrypted Emit

emit("secure-data", {value: "secret"}, {encrypt: true});

Enable encryption in provider:

<SocketProvider
    url="http://localhost:3000"
    encryptionKey="my-secret-key"
    useEncryption={true}
>
    <App/>
</SocketProvider>

Server must also decrypt the payload using the same AES key.


⚙️ Provider Options

| Prop | Type | Description | |-------------------|-----------------------------------|--------------------------------------| | url | string | Required socket server URL | | namespace | string? | Socket.IO namespace | | getToken | () => string \| Promise<string> | JWT token retriever | | onUnauthorized | () => string \| Promise<string> | Token refresh when unauthorized | | maxRetries | number | Max reconnection attempts | | initialDelayMs | number | Backoff initial delay | | backoffFactor | number | Backoff multiplier | | persistQueue | boolean | Save queue in localStorage | | queueKey | string | Storage key name | | queueTTL | number | Expiry in ms for stored queue | | maxQueueSize | number | Maximum offline queue size | | onQueueOverflow | (event) => void | Called when queue exceeds max size | | useEncryption | boolean | Enable payload encryption | | encryptionKey | string | AES key for encryption | | debug | boolean | Log connection/retry/status info | | extraHeaders | record | add custom extra header when connect |


🧰 Hooks API

useSocketContext()

Returns:

  • socket: active socket instance
  • connected: current connection status
  • emit(event, data, options): emit with ack/encryption
  • queue: access queued events
  • latency: current latency in ms

useEvent(event, handler)

Subscribes to a specific event and removes it automatically on unmount.

useLatency()

Returns current ping/pong latency (updated every 5s).

useConnectionStatus()

Returns the current socket lifecycle status, one of:

  • "connecting"
  • "connected"
  • "reconnecting"
  • "disconnected"
  • "unauthorized"
  • "failed"

Example usage:

import {useConnectionStatus} from "socket.io-react-hooks-advanced";

function StatusTag() {
    const status = useConnectionStatus();
    return <p>Status: <strong>{status}</strong></p>;
}

🧱 Example: Multi-Namespace Setup

const notifSocket = createNamespacedSocket();
const gameSocket = createNamespacedSocket();

<notifSocket.Provider url="..." namespace="/notif">
    <gameSocket.Provider url="..." namespace="/game">
        <App/>
    </gameSocket.Provider>
</notifSocket.Provider>

Each namespaced socket provider is fully isolated.


📦 Offline Queue Handling

  • Emit calls made when disconnected are pushed to a queue
  • On reconnect, queued emits are flushed to server
  • Optionally persisted in localStorage with TTL
  • Overflow handler called if queue exceeds maxQueueSize

Queue is type-safe and timestamped for tracing/debugging.


🧩 Middleware Support

Register middleware to intercept emit or received events:

registerMiddleware({
    onOutgoing: (event, data) => encrypt(data),
    onIncoming: (event, data) => decrypt(data),
});

Scoped middleware can also be registered per namespace.


🔐 AES Encryption

AES-256 encryption is implemented using crypto-js. Encryption must be enabled with a shared key:

<SocketProvider
    url="..."
    encryptionKey="shared-secret-key"
    useEncryption={true}
>
    <App/>
</SocketProvider>

How it works:

  • emit(..., { encrypt: true }) wraps payload
  • Middleware encrypts using AES
  • Server must decrypt with same key (Node.js: crypto)

🌐 TypeScript Support

All types are exposed and extensible:

  • SocketContextType
  • QueuedEmit
  • SocketProviderProps
  • NamespacedSocket
  • SocketEmitOptions

Works out of the box with strict TypeScript settings.


🧪 Example: Emit with ACK and Timeout

emit("joinRoom", {roomId: 123}, {
    ack: (response) => console.log("joined", response),
    timeout: 3000,
});

📜 License

MIT © Jingx


Feel free to contribute, submit issues, or request enhancements!

Built with ❤️ for scalable, socket-driven apps.