# Connection state exposed to UI as connected, connecting, or disconnected indicator

- **Pattern:** `ab-000750` (`community-realtime.realtime-ux.connection-state-ui`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000750
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000750)

## Why it matters

When the realtime socket drops and the UI keeps rendering as if everything is fine, users type messages into a dead channel, assume their posts sent, and discover hours later that nothing reached the server. That silent failure mode destroys trust in the product, generates duplicate support tickets, and corrupts conversation ordering once reconnection flushes a backlog of stale sends. A visible connection indicator is the primary user-experience affordance that distinguishes a working realtime feature from one the user has to guess at, and its absence is the top cause of reported bugs in chat and collaboration tools.

## Severity rationale

Low because the defect is a UX gap with no security impact, though it directly drives support load and silent message loss.

## Remediation

Track the socket's lifecycle in React state and render a distinct visual for each of connected, connecting, and disconnected. Subscribe to `connect`, `disconnect`, and `connect_error` in a single effect inside a component like `src/components/ConnectionIndicator.tsx`, and return the subscription cleanup so stale handlers do not leak across remounts.

```tsx
const [state, setState] = useState<'connected' | 'connecting' | 'disconnected'>('connecting');
useEffect(() => {
  socket.on('connect', () => setState('connected'));
  socket.on('disconnect', () => setState('disconnected'));
  socket.on('connect_error', () => setState('disconnected'));
  return () => { socket.off('connect'); socket.off('disconnect'); socket.off('connect_error'); };
}, []);
```

## Detection

- **ID:** `connection-state-ui`
- **Severity:** `low`
- **What to look for:** Count all connection state display elements in the UI. Enumerate the states handled: connected, connecting, reconnecting, disconnected. For each state, classify whether a distinct visual indicator is rendered.
- **Pass criteria:** The UI displays a connection state indicator that updates when the connection status changes. At least 3 distinct states must be visually distinguishable (connected, connecting, disconnected). Report even on pass: "UI shows N distinct connection states."
- **Fail criteria:** No connection state is displayed to the user, or it is displayed but does not update on disconnect/reconnect.
- **Skip (N/A) when:** Never — users need to know if real-time features are working.
- **Detail on fail:** `"No connection indicator in the UI. Users don't know if they are offline or why messages aren't sending."`
- **Remediation:** Display connection state to users:

  ```tsx
  const [connectionState, setConnectionState] = useState<'connected' | 'connecting' | 'disconnected'>('connecting');

  useEffect(() => {
    socket.on('connect', () => setConnectionState('connected'));
    socket.on('disconnect', () => setConnectionState('disconnected'));
    socket.on('connect_error', () => setConnectionState('disconnected'));

    return () => {
      socket.off('connect');
      socket.off('disconnect');
      socket.off('connect_error');
    };
  }, []);

  return (
    <div className="connection-indicator">
      {connectionState === 'connected' && <span className="status-online">●</span>}
      {connectionState === 'connecting' && <span className="status-connecting">◐</span>}
      {connectionState === 'disconnected' && <span className="status-offline">○</span>}
    </div>
  );
  ```

Taxons: user-experience

HTML version: https://auditbuffet.com/patterns/ab-000750
