# WebSockets
The WebSocket extension enables real-time, bidirectional communication with [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications) servers directly from HTML. It manages connections efficiently through reference counting, automatic reconnection, and seamless integration with htmx's swap and event model.
## Installing
```html
<script src="/path/to/htmx.js"></script>
<script src="/path/to/ext/hx-ws.js"></script>
```
For npm-style build systems:
```javascript
import 'htmx.org';
import 'htmx.org/dist/ext/hx-ws.js';
```
## Usage
| Attribute | Description |
|-----------|-------------|
| `hx-ws:connect="<url>"` | Establishes a WebSocket connection to the specified URL |
| `hx-ws:send` | Sends form data or [`hx-vals`](https://four.htmx.org/reference/attributes/hx-vals) to the WebSocket on trigger |
| `hx-ws:send="<url>"` | Like `hx-ws:send` but creates its own connection to the URL |
**JSX-Compatible Variants:** For frameworks that don't support colons in attribute names, use hyphen variants: `hx-ws-connect` and `hx-ws-send`.
### Basic Example
```html
<div hx-ws:connect="/chatroom" hx-target="#messages" hx-swap="beforeend">
<div id="messages"></div>
<form hx-ws:send>
<input name="message" placeholder="Type a message...">
<button type="submit">Send</button>
</form>
</div>
```
This example:
1. Establishes a WebSocket connection to `/chatroom` when the page loads
2. Appends incoming HTML messages to `#messages`
3. Sends form data as JSON when the form is submitted
## Receiving Messages
### JSON Envelope Format
Messages from the server should be JSON objects:
```json
{
"channel": "ui",
"format": "html",
"target": "#notifications",
"swap": "beforeend",
"payload": "<div class='notification'>New message!</div>",
"request_id": "abc-123"
}
```
| Field | Default | Description |
|-------|---------|-------------|
| `channel` | `"ui"` | Message routing channel |
| `format` | `"html"` | Content format |
| `target` | Element's [`hx-target`](https://four.htmx.org/reference/attributes/hx-target) | CSS selector for target element |
| `swap` | Element's [`hx-swap`](https://four.htmx.org/reference/attributes/hx-swap) | Swap strategy (innerHTML, beforeend, etc.) |
| `payload` | — | The content to swap |
| `request_id` | — | Matches response to original request |
**Minimal Example** (using all defaults):
```json
{"payload": "<div>Hello World</div>"}
```
### Channels
- **`ui` channel** (default): HTML content is swapped into the target element using htmx's swap pipeline
- **Custom channels**: Emit an `htmx:wsMessage` event for application handling
```javascript
document.addEventListener('htmx:wsMessage', (e) => {
if (e.detail.channel === 'notifications') {
showNotification(e.detail.payload);
}
});
```
## Sending Messages
When an element with `hx-ws:send` is triggered, the extension sends a JSON message:
```json
{
"type": "request",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"event": "submit",
"headers": {
"HX-Request": "true",
"HX-Current-URL": "https://example.com/chat"
},
"values": {
"message": "Hello!"
},
"path": "wss://example.com/chatroom",
"id": "chat-form"
}
```
### Modifying Messages Before Send
```javascript
document.addEventListener('htmx:before:ws:send', (e) => {
e.detail.data.headers['Authorization'] = 'Bearer ' + getToken();
if (!isValid(e.detail.data)) {
e.preventDefault();
}
});
```
## Configuration
Configure the extension via `htmx.config.websockets`:
```javascript
htmx.config.websockets = {
reconnect: true, // Enable auto-reconnect (default: true)
reconnectDelay: 1000, // Initial reconnect delay in ms (default: 1000)
reconnectMaxDelay: 30000, // Maximum reconnect delay in ms (default: 30000)
reconnectJitter: true, // Add randomization to delays (default: true)
pendingRequestTTL: 30000 // Time-to-live for pending requests in ms (default: 30000)
};
```
### Reconnection Strategy
The extension uses exponential backoff with optional jitter:
- **Base formula**: `delay = min(reconnectDelay * 2^(attempts-1), reconnectMaxDelay)`
- **Jitter**: Adds +/-25% randomization to avoid thundering herd
- **Reset**: Attempts counter resets to 0 on successful connection
## Connection Management
### Reference Counting
Multiple elements can share a single WebSocket connection:
```html
<div hx-ws:connect="/notifications" id="notif-1"></div>
<div hx-ws:connect="/notifications" id="notif-2"></div>
```
When all elements using a connection are removed from the DOM, the connection is automatically closed.
## Events
### Connection Lifecycle
| Event | Cancelable | Detail | Description |
|-------|------------|--------|-------------|
| `htmx:before:ws:connect` | Yes | `{url}` | Before establishing connection |
| `htmx:after:ws:connect` | No | `{url, socket}` | After successful connection |
| `htmx:ws:close` | No | `{url, code, reason}` | When connection closes |
| `htmx:ws:error` | No | `{url, error}` | On connection error |
| `htmx:ws:reconnect` | No | `{url, attempts}` | Before reconnection attempt |
### Message Events
| Event | Cancelable | Detail | Description |
|-------|------------|--------|-------------|
| `htmx:before:ws:send` | Yes | `{data, element, url}` | Before sending (data is modifiable) |
| `htmx:after:ws:send` | No | `{data, url}` | After message sent |
| `htmx:before:ws:message` | Yes | `{envelope, element}` | Before processing received message |
| `htmx:after:ws:message` | No | `{envelope, element}` | After processing received message |
| `htmx:wsMessage` | No | `{channel, format, payload, ...}` | For non-UI channel messages |
## Examples
### Live Chat
```html
<div hx-ws:connect="/chat">
<div id="messages" hx-target="this" hx-swap="beforeend"></div>
<form hx-ws:send>
<input name="message" placeholder="Message..." autocomplete="off">
<button type="submit">Send</button>
</form>
</div>
```
### Real-Time Dashboard
```html
<div hx-ws:connect="/dashboard">
<div id="cpu-usage">--</div>
<div id="memory-usage">--</div>
</div>
```
Server sends targeted updates:
```json
{"target": "#cpu-usage", "payload": "<span>45%</span>"}
{"target": "#memory-usage", "payload": "<span>2.3 GB</span>"}
```
## Upgrading from htmx 2.x
### Attribute Changes
| Old (htmx 2.x) | New (htmx 4.x) | Notes |
|----------------|----------------|-------|
| `ws-connect="<url>"` | `hx-ws:connect="<url>"` | Or `hx-ws-connect` for JSX |
| `ws-send` | `hx-ws:send` | Or `hx-ws-send` for JSX |
### Event Changes
| Old Event | New Event | Notes |
|-----------|-----------|-------|
| `htmx:wsOpen` | `htmx:after:ws:connect` | Different detail structure |
| `htmx:wsClose` | `htmx:ws:close` | Now includes `code` and `reason` |
| `htmx:wsError` | `htmx:ws:error` | Similar |
| `htmx:wsBeforeMessage` | `htmx:before:ws:message` | Different detail structure |
| `htmx:wsAfterMessage` | `htmx:after:ws:message` | Different detail structure |
| `htmx:wsConfigSend` | `htmx:before:ws:send` | Modify `e.detail.data` instead |
| `htmx:wsAfterSend` | `htmx:after:ws:send` | Similar |
### Message Format Changes
**Send payload** now includes `type`, `request_id`, `event`, and structured `headers` object instead of `HEADERS` string.
**Receive format** now expects JSON envelope with `channel`, `format`, `target`, `swap`, `payload` fields instead of raw HTML or [`hx-swap-oob`](https://four.htmx.org/reference/attributes/hx-swap-oob).
Enable bidirectional real-time communication via WebSockets
The WebSocket extension enables real-time, bidirectional communication with WebSocket servers directly from HTML. It manages connections efficiently through reference counting, automatic reconnection, and seamless integration with htmx’s swap and event model.