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

@desolint/socket-client

v0.0.1

Published

React Socket.IO client provider and hooks for Desol Int. projects

Readme

@desolint/socket-client

React provider and hooks for Socket.IO, with a non-React disconnect hatch.

Requirements

  • React 18 or newer
  • npm 7 or newer — npm 7+ installs peer dependencies automatically

Install

npm install @desolint/socket-client

react and socket.io-client are peer dependencies and npm installs them for you on npm 7+. @desolint/socket-shared is a pinned regular dependency and comes down automatically — you never install it yourself.

yarn add @desolint/socket-client react socket.io-client
# or
pnpm add @desolint/socket-client react socket.io-client

Why these are peer dependencies, not regular ones

Two copies of React break hooks outright — React keeps hook state in module-level internals, so a component rendered by one copy using a hook from another throws Invalid hook call. This package ships a provider and hooks, so it must use your React.

socket.io-client holds the live connection. A second copy would open a second socket to the same server, so events delivered to one would never reach components listening through the other.

Declaring them as peers means npm reuses the copies your application already has.

Quick start

@desolint/socket-shared is a pinned dependency of this package, not a peer, and its types and values (SocketContract, AUTH_FAILED_MESSAGE, isAuthFailure, etc.) are re-exported here — @desolint/socket-client is the only import specifier you need.

Mount the provider, passing your app's auth state:

'use client';

export default function SocketGate({children}) {
  // enabled:false — read the auth query CACHE, never drive it. See below.
  const {isLoggedIn} = useGetLoggedInUser({enabled: false});

  return <SocketProvider shouldConnect={isLoggedIn}>{children}</SocketProvider>;
}

That wrapper is the only socket code your app owns. It exists because the provider needs your auth state and the package must not fetch it itself.

url defaults to NEXT_PUBLIC_SERVER_URL, which every Desol frontend already sets, and falls back to the page's own origin. Pass url only to override it.

Consume events — subscribe/unsubscribe is handled for you, and an inline handler won't cause a resubscribe on every render:

useSocketEvent<AppContract, 'message'>('message', (text) => {
  showToast({type: 'info', message: text});
});

Send events with the same typed socket useSocket returns — emitting has no subscribe/unsubscribe lifecycle, so it needs no dedicated hook. socket is null until shouldConnect opens a connection, hence the optional chaining:

const {socket} = useSocket<AppContract>();
socket?.emit('helloWorld', 'Hello from client');

Ask for a response instead of firing and forgetting, via socket.io's own emitWithAck — still no dedicated hook, same reasoning as why emitting itself needs none:

const result = await socket?.emitWithAck('chat:send', {recipientId, text});

emitWithAck is only typed for events whose contract signature ends in a callback — an event without one won't offer it, which is exactly the compile-time signal for whether an event supports acks at all.

Disconnect from outside React (a 401 interceptor, a resetAppState util):

import {disconnectSocket} from '@desolint/socket-client';

disconnectSocket(); // no-op when no provider is mounted

Design notes

Two things this package deliberately does not do

It does not fetch auth. shouldConnect is a prop your app controls. A provider that drove its own auth query previously caused an infinite reload loop: an unguarded /users/me on a public route → 401 → interceptor calls resetAppState()window.location.href → remount → refetch. Keeping auth ownership in the app makes this impossible.

It does not force a transport. Native browser WebSocket has no API for custom headers, so extraHeaders only reaches the server over an HTTP request — polling, or the initial handshake before upgrading. A project authenticating with a custom header, not a cookie, must not force transports: ['websocket'].

Cookie auth is unaffected either way: browsers attach cookies to a WebSocket handshake automatically, by the cookie's own Domain/SameSite/Secure rules, not by transport. What cookie auth does need is withCredentials: true — already set in DEFAULT_CLIENT_OPTIONS — so the initial cross-site request carries the cookie at all.

Behaviour

  • shouldConnect is the only thing that opens a connection. There's no connect() on the context — two owners of one connection meant a manually opened socket the provider's effect would never tear down.
  • Connects when shouldConnect turns true; disconnects when it turns false.
  • Disconnects on unmount.
  • Stops reconnecting after a connect_error carrying AUTH_FAILED_MESSAGE. Other errors still retry — the default is infinite reconnection, so without this, an expired cookie would hammer the server forever.
  • Guards SSR: connecting is a no-op when window is undefined.
  • Stopping reconnection is silent to your app. To react yourself — e.g. show "session expired" — check a connect_error's error against the re-exported isAuthFailure on the socket useSocket() returns, and clean up the listener the same way useSocketEvent does.
  • The contract type (useSocket<AppContract>(), useSocketEvent<AppContract, E>()) is applied per call, not enforced by SocketProvider — nothing stops one component specifying a different contract than another against the same provider.

API

| Export | Notes | | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | SocketProvider | Props:children, shouldConnect, and optionally url, options, socketClient. | | useSocket<T>() | {socket, isConnected, disconnect}. Throws outside a provider. socket.emit(...) sends client→server events, typed from the contract's clientToServer map. | | useSocketEvent<T, E>(event, handler) | Subscribes for the component's lifetime. | | disconnectSocket() | Non-React hatch. Disconnects every mounted provider; no-op when none is mounted. | | DEFAULT_CLIENT_OPTIONS | Whatoptions is merged over. |

disconnect exists so logout can drop the socket before the cookie is cleared, without waiting for shouldConnect to flip.

socketClient is a test seam — pass a stub factory to drive the provider without a server.

Development

npm install        # install dependencies (from the repo root)
npm run build      # build all three packages
npm test           # type-check + jest
npm run lint       # eslint

This package is part of the package-socket-io workspaces monorepo — run the commands from the repository root, not this directory.


License

MIT © Desolint — see LICENSE.

Free to use, modify and redistribute, commercially or otherwise. Provided "as is", without warranty or liability of any kind.