esc

Type to search...

Reconnection

When a connection drops unexpectedly, the manager automatically attempts to reconnect. Subscriptions are restored, in-flight messages are cleaned up, and your app stays in sync.

How It Works

On an unexpected close (any code other than 1000), the manager:

  1. Drops any in-flight messages and notifies useSocketInFlightDrop listeners
  2. Clears any pending subscriptions
  3. Sets the state to "reconnecting"
  4. Waits using exponential backoff with jitter
  5. Opens a new connection
  6. Re-sends all active subscriptions
  7. Fires onReady with the list of restored subscription keys

If disconnect() was called, or the server closed with code 1000 (normal close), no reconnection is attempted.

Exponential Backoff

Each retry waits longer than the last, with a random 0–1000ms jitter to prevent all clients from reconnecting at the same instant:

formula
delay = min(baseDelay * 2^attempt + random(0, 1000ms), maxDelay)

With the default settings (base 1s, cap 30s, 10 attempts):

  • Attempt 1: ~1–2s
  • Attempt 2: ~2–3s
  • Attempt 3: ~4–5s
  • Attempt 4: ~8–9s
  • Attempt 5: ~16–17s
  • Attempts 6–10: 30s (capped)

Total worst-case time before giving up: ~3 minutes. Pass reconnectMaxAttempts: Number.POSITIVE_INFINITY to retry forever (useful for long-lived apps that prefer to surface a stale-connection banner instead of a terminal "disconnected" state).

Configuration

manager.ts
const manager = new WebSocketManager<TClientMsg, TServerMsg>({
  // ...

  // Maximum number of reconnect attempts (default: 10)
  reconnectMaxAttempts: 10,

  // Initial delay before the first retry (default: 1000ms)
  reconnectBaseDelayMs: 1_000,

  // Maximum delay between retries (default: 30000ms)
  reconnectMaxDelayMs: 30_000,
});

Online / Offline Detection

The manager listens to the browser's online and offline events:

  • Offline — the connection is immediately closed and the state moves to "reconnecting"
  • Online — the reconnect attempt counter resets to zero and a fresh attempt is scheduled at the base delay (~1–2s)

Force Reconnect

Sometimes you need to tear everything down and start fresh — for example after a token refresh or when switching authenticated contexts:

reconnect.ts
manager.forceReconnect();

This drops all in-flight messages, clears pending subscriptions, closes the current connection, resets the attempt counter to zero, and immediately reopens the socket. The state goes connected → connecting in a single transition — there is no transient "disconnected" frame for subscribers to flicker on.

Subscription Restoration

After reconnecting, the manager re-sends every active subscription using the subscribe payload from the first subscriber for that key (first-payload wins — see Subscriptions). Your components don't need to track or re-trigger anything. Restored keys are sent back-to-back; if your app holds many subscriptions per user, prefer a server-side multi-subscribe envelope or stagger the restore yourself in onReady.

The onReady callback fires after all subscriptions have been re-sent. Use it to fetch any data you may have missed while disconnected:

manager.ts
const manager = new WebSocketManager<TClientMsg, TServerMsg>({
  // ...
  onReady() {
    // Connection is open and subscriptions are restored.
    // Good place to sync any missed state.
    console.log("Reconnected and ready");
  },
});

Connection States

Track the connection state in your UI with the useSocketConnectionState hook:

ConnectionStatus.tsx
import { useSocketConnectionState } from "@luciodale/react-socket";

function ConnectionStatus() {
  const state = useSocketConnectionState(manager);

  const labels = {
    idle: "Not connected",
    disconnected: "Offline",
    connecting: "Connecting...",
    connected: "Connected",
    reconnecting: "Reconnecting...",
  } as const;

  return <span>{labels[state]}</span>;
}

Next Steps