A React hook for handling Server-Sent Events (SSE) connections with automatic retry, error handling, and TypeScript support.
- 🔄 Automatic reconnection with exponential backoff
- 🎯 TypeScript support with generics for type-safe data handling
- ⚡ Custom event handling
- 🔍 Comprehensive error handling
- ⏱️ Configurable retry strategy with jitter
- 🧩 Custom data parsing support
- 🎮 Manual connection control
import { useServerSentEvents } from 'shared/hooks/useServerSentEvents';
function MyComponent() {
const { data, status, error } = useServerSentEvents<{ message: string }>(
'http://api.example.com/events'
);
if (status === 'ERROR') {
return <div>Error: {error?.message}</div>;
}
return (
<div>
<p>Status: {status}</p>
{data && <p>Message: {data.message}</p>}
</div>
);
}
useServerSentEvents<T>(url: string, options?: UseSSEOptions<T>): UseSSEResult<T>
The URL of the SSE endpoint.
Option | Type | Default | Description |
---|---|---|---|
onMessage |
(event: MessageEvent) => void |
undefined |
Callback for handling raw message events |
onError |
(error: Event) => void |
undefined |
Callback for handling error events |
onOpen |
() => void |
undefined |
Callback when connection is established |
onClose |
() => void |
undefined |
Callback when connection is closed |
withCredentials |
boolean |
false |
Enable credentials for cross-origin requests |
eventName |
string |
'message' |
Custom event name to listen for |
parseData |
(data: string) => T |
JSON.parse |
Custom data parsing function |
retry |
RetryConfig |
See below | Configuration for retry behavior |
interface RetryConfig {
maxAttempts: number; // Maximum number of retry attempts
initialDelayMs: number; // Initial delay before first retry
maxDelayMs: number; // Maximum delay between retries
backoffFactor: number; // Exponential backoff multiplier
jitterFactor?: number; // Random jitter factor (0-1)
shouldRetry?: (error: SSEError, attemptCount: number) => boolean;
}
Default retry configuration:
const DEFAULT_RETRY_CONFIG = {
maxAttempts: 3,
initialDelayMs: 1000,
maxDelayMs: 30000,
backoffFactor: 2,
jitterFactor: 0.1,
shouldRetry: () => true,
};
The hook returns an object with the following properties:
interface UseSSEResult<T> {
status: 'CONNECTING' | 'OPEN' | 'CLOSED' | 'ERROR';
data: T | null;
error: SSEError | null;
close: () => void;
reconnect: () => void;
retryCount: number;
}
Property | Type | Description |
---|---|---|
status |
SSEStatus |
Current connection status |
data |
T | null |
Latest received data, parsed according to parseData |
error |
SSEError | null |
Error object if an error occurred |
close |
() => void |
Function to manually close the connection |
reconnect |
() => void |
Function to manually trigger reconnection |
retryCount |
number |
Number of retry attempts made |
const { data } = useServerSentEvents<UpdateData>(
'http://api.example.com/updates',
{
eventName: 'update',
onMessage: (event) => {
console.log('Raw event:', event);
},
}
);
const { data } = useServerSentEvents<UserStatus>(
'http://api.example.com/status',
{
parseData: (data) => {
const parsed = JSON.parse(data);
return {
...parsed,
lastUpdate: new Date(parsed.timestamp),
};
},
}
);
const { status, error } = useServerSentEvents('http://api.example.com/events', {
retry: {
maxAttempts: 5,
initialDelayMs: 2000,
maxDelayMs: 60000,
backoffFactor: 1.5,
shouldRetry: (error, attemptCount) => {
// Don't retry on 404 errors
if (error.code === 'NOT_FOUND') return false;
return attemptCount < 5;
},
},
});
function StatusMonitor() {
const { status, close, reconnect } = useServerSentEvents(
'http://api.example.com/status'
);
return (
<div>
<p>Connection: {status}</p>
<button onClick={close}>Stop Monitoring</button>
<button onClick={reconnect}>Reconnect</button>
</div>
);
}
The hook provides detailed error information through the SSEError
class:
class SSEError extends Error {
code?: string;
}
Common error codes:
CONNECTION_ERROR
: General connection failurePARSE_ERROR
: Failed to parse incoming dataMAX_RETRY_EXCEEDED
: Maximum retry attempts reached
-
Type Safety: Always provide a type parameter for better type inference:
interface EventData { type: string; payload: unknown; } const { data } = useServerSentEvents<EventData>(url);
-
Error Handling: Always handle error states in your UI:
if (status === 'ERROR') { return <ErrorComponent error={error} onRetry={reconnect} />; }
-
Cleanup: The hook automatically handles cleanup, but make sure to call
close()
when manually ending connections. -
Reconnection Strategy: Configure the retry strategy based on your needs:
- Increase
maxAttempts
for critical connections - Adjust
jitterFactor
to prevent thundering herd problems - Use
shouldRetry
to implement custom retry logic
- Increase
The hook is fully typed and supports generic type parameters for the event data:
interface UserData {
firstName: string;
lastName: string;
email: string;
}
const { data } = useServerSentEvents<UserData>('http://api.example.com/events');