@ibnlanre/socket is a fast, lightweight, type-safe JSON WebSocket client for React — cache-first, pool-aware, and schema-safe. It also ships an EventSourceClient for Server-Sent Events.
Define one client per endpoint, let identical params reuse a single pooled connection across components, and plug in any Standard Schema–compatible validator (Zod, Valibot, ArkType, …) for runtime validation.
The full documentation lives at the docs site:
→ https://use-socket.vercel.app
| Guides | Reference |
|---|---|
| Introduction · Getting started · Mental model | SocketClient · Socket · SocketCache |
| Validation · Caching · Reconnection · Server-Sent Events | EventSourceClient · Options · Types & constants |
The docs are a VitePress site in docs/; run them locally with:
pnpm --filter @ibnlanre/socket-docs dev- Cache-first state — previously received data surfaces instantly from the Cache API while fresh messages stream in (stale-while-reconnect, no empty flashes).
- One connection per endpoint — identical params reuse the same pooled
Socket, so many components share a single live connection. - Runtime-safe messaging — validate params, outgoing payloads, and incoming messages with any Standard Schema validator; asynchronous schemas are supported through the matching async APIs.
- Predictable lifecycle — distinct
status/fetchStatusdimensions, derived flags, and explicit ownership (closevsdisposevsevict). - Built-in reconnection — backoff, jitter, custom retry conditions, and recovery on network restore, window focus, and bfcache page restore.
- Server-Sent Events — native
EventSource(GET) and fetch streaming (any method), named events, async iteration.
# npm
npm install @ibnlanre/socket
# pnpm
pnpm add @ibnlanre/socket
# yarn
yarn add @ibnlanre/socketValidation is optional — bring your own Standard Schema validator when you want it:
pnpm add @ibnlanre/socket zod # or valibot, arktype, …The smallest useful setup: create one client, call useSocket, render the selected data.
import { SocketClient } from "@ibnlanre/socket";
const priceClient = new SocketClient<string>({
baseURL: "wss://example.com",
url: "/prices",
});
export function PriceTicker() {
const price = priceClient.useSocket({
select: (message) => message ?? "Waiting for price…",
});
return <div>{price.data}</div>;
}Need the imperative socket instead?
const socket = await priceClient.get({ symbol: "BTC" });
socket.open();
await socket.waitUntil("open");
await socket.send({ type: "subscribe", symbol: "BTC" });- One
SocketClient= one endpoint. Schemas, cache, and reconnection live on the client. - Same params ⇒ same socket. Connections are pooled by a fully resolved URL; parameters are validated/normalized once and that exact value drives both the pool key and the connection URL.
- Hook for React, instance for imperative code.
useSocketreturns a read-only state, selected data, andsend;await client.get()returns the managedSocket. - Ownership is explicit.
socket.close()disconnects but preserves subscriptions (a lateropen()reconnects);socket.dispose()/await client.evict(params)release the socket permanently. - Sends go through an ordered queue.
await send()supports sync and async schemas; waiting payloads are bounded bymaxQueueSize/queueMaxAge/queueOverflow. - Async validation, cancellable.
await client.get(input, { signal })resolves parameters before returning the pooled socket;useSocketowns that same resolution in React and exposesisPreparinganderror. Schema input and output types may differ.
See Mental model for the full picture.
| Export | Kind | Purpose |
|---|---|---|
SocketClient |
class | Pool + React hooks for one endpoint (useSocket, get, evict, clear, dispose) |
Socket |
class | One managed connection — state snapshot + commands (open, close, send, on, waitUntil, dispose) |
SocketCache |
class | In-memory mirror + Cache API persistence per origin |
EventSourceClient |
class | One-way Server-Sent Events client (native GET + fetch streaming) |
SocketCloseCode / SocketCloseReason |
const | WebSocket close-code enums and reasons |
| Types | type | SocketState, SocketCommands, UseSocketResult, SocketDiagnostic, SocketQueueOptions, … |
Monorepo: app (the library + tests), docs (VitePress site), tests (browser e2e).
pnpm install # install the workspace
# unit tests + typecheck (app)
pnpm --dir app test
# browser e2e (Playwright; keep port 4173 free — it reuses an existing server)
pnpm test:e2e
# docs
pnpm --filter @ibnlanre/socket-docs dev # local docs site
pnpm --filter @ibnlanre/socket-docs build # build docs@ibnlanre/socket is licensed under the BSD-3 License. See the LICENSE file for details.
socket.close() disconnects while preserving subscriptions. socket.dispose() is permanent; client.evict(params) disposes and removes a pooled instance. Queues and pools are bounded. onDiagnostic exposes retry, queue, validation, and cache activity; prepareConnection supports fresh credentials before every transport attempt.
See migration notes for changed encoding, queue, and ownership behavior.