react-websocket 2.2.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,84 +1,168 @@
1
- # react-websocket [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/mehmetkose/react-websocket/edit/master/README.md) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmehmetkose%2Freact-websocket.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmehmetkose%2Freact-websocket?ref=badge_shield)
1
+ # react-websocket
2
2
 
3
- `react-websocket` is a easy-to-use React component for websocket communications.
3
+ **Hooks-first WebSocket client for React 18 and 19.** Shared connections, automatic reconnects, a send queue, typed and validated messages, and Suspense, in about 2.5 kB with zero dependencies.
4
4
 
5
- ## Help Wanted
5
+ [![npm](https://img.shields.io/npm/v/react-websocket.svg)](https://www.npmjs.com/package/react-websocket)
6
+ [![CI](https://github.com/mehmetkose/react-websocket/actions/workflows/ci.yml/badge.svg)](https://github.com/mehmetkose/react-websocket/actions/workflows/ci.yml)
7
+ [![bundle size](https://img.shields.io/bundlejs/size/react-websocket)](https://bundlejs.com/?q=react-websocket)
8
+ [![types](https://img.shields.io/npm/types/react-websocket.svg)](https://www.npmjs.com/package/react-websocket)
9
+ [![license](https://img.shields.io/npm/l/react-websocket.svg)](LICENSE)
10
+ [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmehmetkose%2Freact-websocket.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmehmetkose%2Freact-websocket?ref=badge_shield)
6
11
 
7
- Things here are running very slowly as I have a lot of other stuff to take care at the moment so please don't be upset if I don't answer your question or if a PR sits unreviewed for a few days or weeks. Anyone interested in helping it move faster can help by submitting or reviewing PR's and answering each other's questions.
12
+ **[Documentation](https://mehmetkose.github.io/react-websocket/)** · [Getting started](https://mehmetkose.github.io/react-websocket/guide/getting-started) · [API](https://mehmetkose.github.io/react-websocket/api/) · [Recipes](https://mehmetkose.github.io/react-websocket/recipes) · [Changelog](CHANGELOG.md)
8
13
 
14
+ - **Shared connections.** Components using the same URL share one socket. StrictMode, `<Activity>` and route transitions never open duplicates.
15
+ - **Auto-reconnect.** Exponential backoff with jitter, plus an immediate retry when the browser comes back online or the tab becomes visible. Optional heartbeats detect dead links.
16
+ - **Send queue.** `send()` before the socket is open just works. The message goes out on connect.
17
+ - **Typed messages.** `json: true`, your own codec, or validation with any [Standard Schema](https://standardschema.dev) library (zod, valibot, arktype), with inferred types.
18
+ - **Suspense.** `use(client.ready)` waits for the first connection, and reconnects never flash a fallback.
19
+ - **Topic subscriptions.** Reference-counted `subscribe`/`unsubscribe` messages that are sent again after every reconnect, so feeds don't silently stop.
20
+ - **Message state.** Reduce messages into a store, and let each component re-render only when its slice changes.
21
+ - **SSR-safe.** Never connects during a server render, hydrates cleanly, and ships a `'use client'` entry for Next.js and other Server Components frameworks.
22
+ - **Small and typed.** Zero dependencies, ESM + CJS, TypeScript declarations with inline docs, plus a React-free `react-websocket/core` entry.
9
23
 
10
- ### Installing
24
+ ## Install
11
25
 
26
+ ```sh
27
+ npm install react-websocket
12
28
  ```
13
- npm install --save react-websocket
29
+
30
+ ## Quick start
31
+
32
+ ```tsx
33
+ import { useState } from 'react';
34
+ import { useWebSocket } from 'react-websocket';
35
+
36
+ type ChatIn = { user: string; text: string };
37
+ type ChatOut = { text: string };
38
+
39
+ function Chat() {
40
+ const [messages, setMessages] = useState<ChatIn[]>([]);
41
+ const { status, send } = useWebSocket<ChatIn, ChatOut>('wss://api.example.com/chat', {
42
+ json: true,
43
+ onMessage: message => setMessages(list => [...list, message])
44
+ });
45
+
46
+ return (
47
+ <>
48
+ <p>{status}</p>
49
+ <ul>
50
+ {messages.map((m, i) => (
51
+ <li key={i}>
52
+ {m.user}: {m.text}
53
+ </li>
54
+ ))}
55
+ </ul>
56
+ <button onClick={() => send({ text: 'hi' })}>Say hi</button>
57
+ </>
58
+ );
59
+ }
14
60
  ```
15
61
 
16
- ### Usage
62
+ Handlers always see your latest props and state, and changing them never reconnects. `send`, `close` and `reconnect` keep the same identity between renders.
63
+
64
+ ## Shared clients
65
+
66
+ Define a connection once and use it anywhere, including outside React:
17
67
 
18
- ```js
19
- import React from 'react';
20
- import Websocket from 'react-websocket';
68
+ ```tsx
69
+ import { createWebSocket, useWebSocketEvent, useWebSocketStatus } from 'react-websocket';
21
70
 
22
- class ProductDetail extends React.Component {
71
+ export const chat = createWebSocket<ChatIn, ChatOut>('wss://api.example.com/chat', { json: true });
23
72
 
24
- constructor(props) {
25
- super(props);
26
- this.state = {
27
- count: 90
28
- };
29
- }
73
+ function Messages() {
74
+ const [messages, setMessages] = useState<ChatIn[]>([]);
75
+ useWebSocketEvent(chat, 'message', m => setMessages(list => [...list, m])); // no re-render per message
76
+ return <ul>{/* … */}</ul>;
77
+ }
78
+
79
+ function Header() {
80
+ const status = useWebSocketStatus(chat); // re-renders only when the status changes
81
+ return <button onClick={() => chat.send({ text: 'hi' })}>Say hi ({status})</button>;
82
+ }
83
+ ```
30
84
 
31
- handleData(data) {
32
- let result = JSON.parse(data);
33
- this.setState({count: this.state.count + result.movement});
34
- }
85
+ The client connects when the first component uses it and closes a tick after the last one unmounts.
35
86
 
36
- render() {
37
- return (
38
- <div>
39
- Count: <strong>{this.state.count}</strong>
87
+ ## Suspense
40
88
 
41
- <Websocket url='ws://localhost:8888/live/product/12345/'
42
- onMessage={this.handleData.bind(this)}/>
43
- </div>
44
- );
45
- }
46
- }
89
+ ```tsx
90
+ import { use, Suspense } from 'react';
47
91
 
48
- export default ProductDetail;
92
+ function Room() {
93
+ use(chat.ready); // React 19: suspend until the first connection
94
+ return <Messages />;
95
+ }
96
+
97
+ <ErrorBoundary fallback={<Retry onClick={() => chat.reconnect()} />}>
98
+ <Suspense fallback={<Spinner />}>
99
+ <Room />
100
+ </Suspense>
101
+ </ErrorBoundary>;
49
102
  ```
50
103
 
51
- ### Properties
104
+ `ready` stays resolved through automatic reconnects, so a network blip shows your `status` UI instead of replacing the page with a spinner. It rejects if the client gives up or `close()` is called.
105
+
106
+ ## API at a glance
52
107
 
53
- #### url
108
+ | Export | |
109
+ | --- | --- |
110
+ | `useWebSocket(url \| client, options?)` | Connect; returns `{ status, lastMessage, attempt, send, close, reconnect, client }` |
111
+ | `useWebSocketEvent(client, type, listener)` | Handle `'open'`, `'message'`, `'close'` or `'error'` without re-rendering |
112
+ | `useWebSocketStatus(client)` | The status; re-renders only when it changes |
113
+ | `useWebSocketTopic(topics, topic)` | Hold topic subscriptions that survive reconnects |
114
+ | `useWebSocketState(store, selector?)` | Read state reduced from messages; re-renders only when the selection changes |
115
+ | `createWebSocket(url, options?)` | A shareable client: `send`, `on`, `connect`, `close`, `reconnect`, `ready`, `getSnapshot`, `subscribe` |
116
+ | `createWebSocketTopics(client, { subscribe, unsubscribe })` | Topic subscription manager for `useWebSocketTopic` |
117
+ | `createWebSocketState(client, { initialState, reduce })` | Message-state store for `useWebSocketState` |
118
+ | `MessageError` | Reported to `onError` when a message fails `parse` or `schema` |
54
119
 
55
- **required**
56
- The url the websocket connection is listening to.
120
+ | Option | Default | |
121
+ | --- | --- | --- |
122
+ | `protocols` | | Sub-protocols for the `WebSocket` constructor |
123
+ | `binaryType` | `'blob'` | |
124
+ | `json` | `false` | `JSON.parse` incoming and `JSON.stringify` outgoing messages |
125
+ | `parse(data)` / `serialize(message)` | | Custom codec; takes precedence over `json` |
126
+ | `schema` | | Standard Schema validation; types are inferred |
127
+ | `reconnect` | `true` | `false`, or `{ maxAttempts, minDelay, maxDelay, factor, jitter, shouldReconnect }` |
128
+ | `heartbeat` | | `{ interval, message, pong, timeout }` |
129
+ | `queue` | `true` | `false`, or `{ limit }` |
130
+ | `logger` | | e.g. `console.debug` |
131
+ | `WebSocket` | global | Custom implementation for tests or Node |
57
132
 
58
- #### onMessage
133
+ `useWebSocket` also takes `onOpen`, `onMessage`, `onClose`, `onError` and `enabled`. The [API reference](https://mehmetkose.github.io/react-websocket/api/) documents everything.
59
134
 
60
- **required**
61
- The callback called when data is received. Data is `JSON.parse`'d
135
+ ## Guides
62
136
 
63
- #### onOpen
137
+ - [Shared clients](https://mehmetkose.github.io/react-websocket/guide/shared-clients) · [Messages and types](https://mehmetkose.github.io/react-websocket/guide/messages) · [Reconnecting and heartbeats](https://mehmetkose.github.io/react-websocket/guide/reconnecting)
138
+ - [Suspense](https://mehmetkose.github.io/react-websocket/guide/suspense) · [Server rendering and Next.js](https://mehmetkose.github.io/react-websocket/guide/ssr) · [StrictMode, Activity and lifecycle](https://mehmetkose.github.io/react-websocket/guide/lifecycle)
139
+ - [Authentication](https://mehmetkose.github.io/react-websocket/guide/authentication) · [Performance](https://mehmetkose.github.io/react-websocket/guide/performance) · [Testing](https://mehmetkose.github.io/react-websocket/guide/testing) · [Without React](https://mehmetkose.github.io/react-websocket/guide/without-react)
140
+ - [Recipes](https://mehmetkose.github.io/react-websocket/recipes) · [Troubleshooting](https://mehmetkose.github.io/react-websocket/troubleshooting) · [Migrating from v2](https://mehmetkose.github.io/react-websocket/migration)
64
141
 
65
- The callback called when the connection is successfully opened.
142
+ The docs source lives in [`docs/`](docs). Run `npm run docs:dev` to browse it locally.
66
143
 
67
- #### onClose
144
+ ## Compatibility
68
145
 
69
- The callback called when the connection is closed either due to server disconnect or network error.
146
+ | | |
147
+ | --- | --- |
148
+ | React | 18 and 19 (`use(client.ready)` needs 19; `<Activity>` is handled on 19.2+). Tested in CI against both |
149
+ | Runtimes | Modern browsers; Node via the `WebSocket` option |
150
+ | Frameworks | Vite, Next.js (App and Pages Router), Remix/React Router, Astro islands, and any React setup |
70
151
 
71
- #### debug
152
+ ## Migrating from v2
72
153
 
73
- default: **false**
74
- Set to **true** to see console logging
154
+ v2 was a `<Websocket>` class component. v3 replaces it with hooks: `<Websocket url onMessage />` becomes `useWebSocket(url, { onMessage })`, and `ref.sendMessage(msg)` becomes `send(msg)`. See the [migration guide](https://mehmetkose.github.io/react-websocket/migration) for the full mapping.
75
155
 
76
- #### reconnect
156
+ ## Example
77
157
 
78
- default: **true**
158
+ [`example/`](example) has a Tornado broadcast server and a Vite + React 19 client.
79
159
 
80
- accelerated reconnection time
160
+ ## Contributing
81
161
 
162
+ Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md). To report a vulnerability, follow [SECURITY.md](SECURITY.md).
82
163
 
83
164
  ## License
165
+
166
+ [MIT](LICENSE)
167
+
84
168
  [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmehmetkose%2Freact-websocket.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fmehmetkose%2Freact-websocket?ref=badge_large)