Skip to content

WebSocket channel with reconnect

Problem. Keep a WebSocket connection alive across network blips with exponential backoff.

Solution. useWebSocket handles reconnect and heartbeats; combine it with useWebSocketChannel for typed framing.

ts
import { effect, useWebSocketChannel } from '@bquery/bquery/reactive';

type OrdersFrame = {
  channel: string;
  data: { type: string; topic?: string; payload?: unknown };
};

const channel = useWebSocketChannel<{ type: 'subscribe'; topic: string }, OrdersFrame>(
  '/realtime',
  {
    protocols: ['app.v1'],
    autoReconnect: { delay: 500, maxDelay: 10_000, maxAttempts: Infinity, factor: 2 },
    heartbeat: { interval: 25_000, message: 'ping' },
  }
);

const orders = channel.subscribe('orders');
effect(() => {
  const frame = orders.data.value;
  if (frame) render(frame.data);
});

// Send typed frames
channel.publish('orders', { type: 'subscribe', topic: 'orders' });

Why it works. Exponential backoff prevents reconnect storms; heartbeats keep proxies from idling out the connection. channel.ws.dispose() tears everything down deterministically.

Released under the MIT License.