React hook for Server-Sent Events with custom headers support
A simple React hook for Server-Sent Events with the key feature that native EventSource lacks: custom headers support.
Perfect for authenticated Server-Sent Events, API keys, and any scenario where you need to send headers with your SSE connection.
npm install react-eventsourceimport React from 'react'
import { useSSE } from 'react-eventsource'
function ServerUpdates() {
const { readyState, close, reconnect } = useSSE({
url: '/api/events',
headers: {
'Authorization': 'Bearer your-token',
'X-API-Key': 'your-api-key'
},
onMessage: (message) => {
console.log('Received:', message.data)
},
onError: (error) => {
console.error('SSE Error:', error)
}
})
const status = ['CONNECTING', 'OPEN', 'CLOSED'][readyState]
return (
<div>
<p>Connection: {status}</p>
<button onClick={close}>Close</button>
<button onClick={reconnect}>Reconnect</button>
</div>
)
}| Property | Type | Required | Description |
|---|---|---|---|
url |
string |
Yes | Server-Sent Events endpoint URL |
headers |
Record<string, string> |
No | Custom headers (auth, API keys, etc.) |
method |
string |
No | HTTP method (defaults to 'GET') |
body |
string | FormData |
No | Request body (rarely needed for SSE) |
onMessage |
(message: EventSourceMessage) => void |
No | Message event handler |
onOpen |
(response: Response) => void |
No | Connection open handler |
onError |
(error: unknown) => void |
No | Error handler |
fetch |
typeof window.fetch |
No | Custom fetch implementation |
openWhenHidden |
boolean |
No | Keep connection open when page is hidden (defaults to true) |
| Property | Type | Description |
|---|---|---|
readyState |
number |
Connection state: 0=CONNECTING, 1=OPEN, 2=CLOSED |
close |
() => void |
Manually close the connection |
reconnect |
() => void |
Close current connection and immediately reconnect |
The hook is fully typed and exports the following types:
import { useSSE, type UseSSEOptions, type UseSSEResult } from 'react-eventsource'
// Type your own variables
const config: UseSSEOptions = {
url: '/api/events',
headers: { 'Authorization': 'Bearer token' }
}
// Extend the interface if needed
interface MySSEOptions extends UseSSEOptions {
retryCount?: number
}useSSE({
url: '/api/user-notifications',
headers: {
'Authorization': `Bearer ${userToken}`
},
onMessage: (msg) => {
const notification = JSON.parse(msg.data)
showNotification(notification)
}
})useSSE({
url: '/api/stream',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-User-ID': userId
},
body: JSON.stringify({
channels: ['updates', 'alerts']
}),
onMessage: handleStreamMessage
})function ChatStream() {
const { readyState, close, reconnect } = useSSE({
url: '/api/chat-stream',
headers: { 'Authorization': `Bearer ${token}` },
onMessage: (msg) => addMessage(JSON.parse(msg.data)),
onError: (err) => console.error('Chat stream error:', err)
})
const isConnected = readyState === 1
return (
<div>
<div className={`status ${isConnected ? 'connected' : 'disconnected'}`}>
{isConnected ? '🟢 Connected' : '🔴 Disconnected'}
</div>
<button onClick={reconnect} disabled={readyState === 0}>
Reconnect
</button>
<button onClick={close}>
Disconnect
</button>
</div>
)
}By default, this hook keeps SSE connections open even when the browser tab/window is hidden (minimized, switched away, etc.). This matches the behavior of the native EventSource API.
If you want to close the connection when the page is hidden and automatically reconnect when visible again (to reduce server load), set openWhenHidden to false:
useSSE({
url: '/api/events',
openWhenHidden: false, // Close connection when page is hidden
onMessage: (msg) => console.log(msg.data)
})When to use openWhenHidden: false:
- You want to reduce server load by closing idle connections
- Your app doesn't need real-time updates when the user isn't viewing the page
- You're implementing a background sync that should pause when hidden
When to keep the default (true):
- You need continuous updates regardless of page visibility
- You're building a real-time monitoring dashboard
- Missing events while hidden would cause data inconsistency
- Native EventSource limitation: Cannot send custom headers
- This hook solves that: Built on
@microsoft/fetch-event-sourcewhich supports headers - React-friendly: Manages connection lifecycle with useEffect
- Simple API: Familiar EventSource-like interface
- TypeScript: Full type safety out of the box
MIT License. See LICENSE for details.