Skip to content
Back to skills

Realtime React Hooks

ASecurity

React hooks for real-time data with SSE, WebSocket, and SWR integration. Covers connection management, reconnection logic, and optimistic updates. Use when building React apps with real-time features. Triggers on SSE hook, WebSocket hook, real-time React, useEventSource, live updates.

  • 11 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added February 10, 2026
developmenttypescriptbashreactapi

Works with

  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned February 12, 2026

npx -y skills add wpank/ai --skill realtime-react-hooks --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Realtime React Hooks?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Realtime React Hooks
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/wpank-realtime-react-hooks/badge)](https://www.skillsdirectory.com/skills/wpank-realtime-react-hooks)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: realtime-react-hooks
model: standard
description: React hooks for real-time data with SSE, WebSocket, and SWR integration. Covers connection management, reconnection logic, and optimistic updates. Use when building React apps with real-time features. Triggers on SSE hook, WebSocket hook, real-time React, useEventSource, live updates.
---

# Real-Time React Hooks

Production patterns for real-time data in React applications using SSE, WebSocket, and SWR.


## Installation

### OpenClaw / Moltbot / Clawbot

```bash
npx clawhub@latest install realtime-react-hooks
```


---

## When to Use

- React apps needing live data updates
- Dashboards with real-time metrics
- Chat interfaces, notifications
- Any UI that should update without refresh

---

## Pattern 1: SSE Hook

```typescript
import { useEffect, useRef, useState, useCallback } from 'react';

interface UseSSEOptions<T> {
  url: string;
  onMessage?: (data: T) => void;
  onError?: (error: Event) => void;
  enabled?: boolean;
}

export function useSSE<T>({
  url,
  onMessage,
  onError,
  enabled = true,
}: UseSSEOptions<T>) {
  const [data, setData] = useState<T | null>(null);
  const [isConnected, setIsConnected] = useState(false);
  const eventSourceRef = useRef<EventSource | null>(null);

  useEffect(() => {
    if (!enabled) return;

    const eventSource = new EventSource(url);
    eventSourceRef.current = eventSource;

    eventSource.onopen = () => {
      setIsConnected(true);
    };

    eventSource.onmessage = (event) => {
      try {
        const parsed = JSON.parse(event.data) as T;
        setData(parsed);
        onMessage?.(parsed);
      } catch (e) {
        console.error('SSE parse error:', e);
      }
    };

    eventSource.onerror = (error) => {
      setIsConnected(false);
      onError?.(error);
    };

    return () => {
      eventSource.close();
      eventSourceRef.current = null;
    };
  }, [url, enabled]);

  const close = useCallback(() => {
    eventSourceRef.current?.close();
    setIsConnected(false);
  }, []);

  return { data, isConnected, close };
}
```

---

## Pattern 2: WebSocket Hook with Reconnection

```typescript
interface UseWebSocketOptions {
  url: string;
  onMessage?: (data: unknown) => void;
  reconnect?: boolean;
  maxRetries?: number;
}

export function useWebSocket({
  url,
  onMessage,
  reconnect = true,
  maxRetries = 5,
}: UseWebSocketOptions) {
  const [isConnected, setIsConnected] = useState(false);
  const wsRef = useRef<WebSocket | null>(null);
  const retriesRef = useRef(0);

  const connect = useCallback(() => {
    const ws = new WebSocket(url);
    wsRef.current = ws;

    ws.onopen = () => {
      setIsConnected(true);
      retriesRef.current = 0;
    };

    ws.onmessage = (event) => {
      try {
        const data = JSON.parse(event.data);
        onMessage?.(data);
      } catch {
        onMessage?.(event.data);
      }
    };

    ws.onclose = () => {
      setIsConnected(false);
      if (reconnect && retriesRef.current < maxRetries) {
        retriesRef.current++;
        const delay = Math.min(1000 * 2 ** retriesRef.current, 30000);
        setTimeout(connect, delay);
      }
    };

    ws.onerror = () => {
      ws.close();
    };
  }, [url, onMessage, reconnect, maxRetries]);

  useEffect(() => {
    connect();
    return () => wsRef.current?.close();
  }, [connect]);

  const send = useCallback((data: unknown) => {
    if (wsRef.current?.readyState === WebSocket.OPEN) {
      wsRef.current.send(JSON.stringify(data));
    }
  }, []);

  return { isConnected, send };
}
```

---

## Pattern 3: SWR with Real-Time Updates

```typescript
import useSWR from 'swr';
import { useEffect } from 'react';

export function useRealtimeData<T>(
  key: string,
  fetcher: () => Promise<T>
) {
  const { data, mutate, ...rest } = useSWR(key, fetcher);

  // Subscribe to real-time updates
  useEffect(() => {
    const eventSource = new EventSource(`/api/events/${key}`);

    eventSource.onmessage = (event) => {
      const update = JSON.parse(event.data);
      
      // Optimistically update cache
      mutate((current) => {
        if (!current) return update;
        return { ...current, ...update };
      }, false); // false = don't revalidate
    };

    return () => eventSource.close();
  }, [key, mutate]);

  return { data, mutate, ...rest };
}
```

---

## Pattern 4: Subscription Hook

```typescript
interface UseSubscriptionOptions {
  channels: string[];
  onEvent: (channel: string, data: unknown) => void;
}

export function useSubscription({ channels, onEvent }: UseSubscriptionOptions) {
  const { send, isConnected } = useWebSocket({
    url: '/api/ws',
    onMessage: (msg: any) => {
      if (msg.type === 'event') {
        onEvent(msg.channel, msg.data);
      }
    },
  });

  useEffect(() => {
    if (!isConnected) return;

    // Subscribe to channels
    channels.forEach((channel) => {
      send({ type: 'subscribe', channel });
    });

    return () => {
      channels.forEach((channel) => {
        send({ type: 'unsubscribe', channel });
      });
    };
  }, [channels, isConnected, send]);

  return { isConnected };
}
```

---

## Pattern 5: Connection Status Indicator

```tsx
export function ConnectionStatus({ isConnected }: { isConnected: boolean }) {
  return (
    <div className="flex items-center gap-2">
      <span
        className={cn(
          'size-2 rounded-full',
          isConnected ? 'bg-success animate-pulse' : 'bg-destructive'
        )}
      />
      <span className="text-xs text-muted-foreground">
        {isConnected ? 'Live' : 'Disconnected'}
      </span>
    </div>
  );
}
```

---

## Related Skills

- **Meta-skill:** [ai/skills/meta/realtime-dashboard/](../../meta/realtime-dashboard/) — Complete realtime dashboard guide
- [resilient-connections](../resilient-connections/) — Retry logic
- [design-systems/animated-financial-display](../../design-systems/animated-financial-display/) — Number animations

---

## NEVER Do

- **NEVER forget cleanup** — Always close connections on unmount
- **NEVER reconnect infinitely** — Use max retries with exponential backoff
- **NEVER parse without try/catch** — Server might send malformed data
- **NEVER mutate and revalidate** — Use `mutate(data, false)` for optimistic updates
- **NEVER ignore connection state** — Show users when they're disconnected

---

## Quick Reference

```typescript
// SSE
const { data, isConnected } = useSSE({ url: '/api/events' });

// WebSocket
const { isConnected, send } = useWebSocket({
  url: 'wss://api.example.com/ws',
  onMessage: (data) => console.log(data),
});

// SWR + Real-time
const { data } = useRealtimeData('metrics', fetchMetrics);

// Subscriptions
useSubscription({
  channels: ['user:123', 'global'],
  onEvent: (channel, data) => updateState(channel, data),
});
```

Files in this skill

  • README.md1.9 KB
  • SKILL.md6.7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.