Realtime React Hooks
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.
MIT-0 · Free to use, modify, and redistribute. No attribution required.
⭐ 0 · 646 · 3 current installs · 3 all-time installs
by@wpank
MIT-0
Security Scan
OpenClaw
Benign
high confidencePurpose & Capability
The name/description state 'real-time React hooks' and the SKILL.md provides only TypeScript/React hook patterns (SSE, WebSocket, SWR integration, subscription, UI indicator). There are no unrelated environment variables, binaries, or permissions requested, so the requested surface matches the stated purpose.
Instruction Scope
SKILL.md contains code examples and usage notes only; it does not instruct the agent to read arbitrary files, export secrets, access unrelated credentials, or post data to unknown endpoints. The code references typical endpoints like '/api/events/${key}' and '/api/ws' which is expected for realtime hooks. There is no open-ended guidance that would grant broad discretionary access.
Install Mechanism
There is no formal install spec (instruction-only), which is lowest risk. README shows manual copy instructions and an 'npx clawhub@latest install' suggestion. One example uses 'npx add https://github.com/.../tree/...' which is not a standard npx pattern — this is a minor inconsistency (documentation quirk) but not evidence of malicious behavior. No downloads or archive extract operations are specified in a machine-executable install spec.
Credentials
The skill declares no required environment variables, no primary credential, and no config paths. The examples don't access process.env or secrets. The lack of requested credentials is proportional to the simple client-side hook patterns provided.
Persistence & Privilege
Flags show always:false and default invocation behavior. As an instruction-only skill it doesn't request persistent presence or modify other skills or system settings. No privileged agent-wide permissions are requested.
Assessment
This skill is a set of example React hooks (no executable install steps), and its code examples are consistent with the described purpose. Before using: (1) verify the source — the skill lists no homepage and the registry owner ID is opaque; prefer installing from a known repository or copying vetted code rather than blindly running install commands that fetch remote code; (2) review and test the hook code in your environment (validate JSON parsing and error handling, and ensure your server endpoints (/api/events, /api/ws) are trusted); (3) avoid pasting or running any non-standard npx/git commands from unknown origins; (4) if you plan to use these patterns in production, run your normal security review (dependency audit, input validation, CORS and auth checks on the server side).Like a lobster shell, security has layers — review code before you run it.
Current versionv1.0.0
Download ziplatest
License
MIT-0
Free to use, modify, and redistribute. No attribution required.
SKILL.md
Real-Time React Hooks
Production patterns for real-time data in React applications using SSE, WebSocket, and SWR.
Installation
OpenClaw / Moltbot / Clawbot
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
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
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
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
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
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/ — Complete realtime dashboard guide
- resilient-connections — Retry logic
- 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
// 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
2 totalSelect a file
Select a file to preview.
Comments
Loading comments…
