A React library for Server-Sent Events (SSE) with TypeScript support
- Full TypeScript support with generics
- React Context API for global SSE state management
- Support for POST requests with custom payloads
- Connection status tracking (INITIALIZING, CONNECTING, OPEN, CLOSED)
- Automatic error handling
- Works with React 18 and 19
npm install react-server-sent-eventsyarn add react-server-sent-eventspnpm add react-server-sent-eventsimport { SSEProvider } from 'react-server-sent-events'
function App() {
return (
<SSEProvider root="http://localhost:8001">
<MyComponent />
</SSEProvider>
)
}import { useSSE } from 'react-server-sent-events'
type MyPayload = { userId: string }
type MyResponse = { message: string; timestamp: string }
function MyComponent() {
const { open, close, isOpen, status, value, error } = useSSE<MyResponse, MyPayload>()
const connect = () => {
open('/api/events', { userId: '123' })
}
return (
<div>
<button onClick={connect} disabled={isOpen}>
Connect
</button>
<button onClick={close} disabled={!isOpen}>
Disconnect
</button>
<p>Status: {status}</p>
<p>Connected: {isOpen ? 'Yes' : 'No'}</p>
{value && <p>Received: {JSON.stringify(value)}</p>}
{error && <p>Error: {error.message}</p>}
</div>
)
}The context provider that manages SSE connections.
| Prop | Type | Description |
|---|---|---|
root |
string |
Base URL for SSE endpoints |
children |
ReactNode |
Child components |
Hook to access SSE functionality. Accepts two generic types:
T- Type of the received valueP- Type of the payload sent when opening connection
| Property | Type | Description |
|---|---|---|
open |
(endpoint: string, payload: P) => void |
Opens an SSE connection |
close |
() => void |
Closes the current connection |
isOpen |
boolean |
Whether the connection is open |
status |
ReadyState |
Current connection status |
value |
T | null |
Last received value |
error |
SseError | null |
Error information if any |
Enum for connection states:
enum ReadyState {
INITIALIZING = -1, // Connection not yet established
CONNECTING = 0, // Connection being established
OPEN = 1, // Connection open and receiving events
CLOSED = 2 // Connection closed
}Note:
XHRStatesis deprecated. UseReadyStateinstead.
The library sends POST requests with the following default headers:
{
'Content-Type': 'application/json',
'X-Event-Stream': 'SSE'
}For advanced use cases, you can use the SSE class directly:
import { SSE, SSEOptionsMethod } from 'react-server-sent-events'
const sse = new SSE('http://localhost:8001/api/events', {
headers: { 'Authorization': 'Bearer token' },
method: SSEOptionsMethod.POST,
payload: JSON.stringify({ data: 'value' }),
withCredentials: true
})
sse.addEventListener('message', (event) => {
console.log('Received:', event.data)
})
sse.addEventListener('error', (event) => {
console.error('Error:', event)
})
sse.stream()
// Later...
sse.close()- Node.js >= 18
# Install dependencies
npm install
# Build the library
npm run build# Terminal 1 - Start the SSE demo server
cd example
npm run server
# Server running at http://localhost:8001
# Terminal 2 - Start the React app
cd example
npm start
# App running at http://localhost:5173The demo server sends events every second with a counter and timestamp.
# Build
npm run build
# Watch mode
npm run dev
# Type checking
npm run typecheck
# Linting
npm run lintThe library includes comprehensive unit tests using Vitest.
# Run tests
npm run test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverageTests cover:
- SSE Class (
src/utils.ts): Connection lifecycle, event parsing, error handling - React Components (
src/index.tsx): SSEProvider, useSSE hook, state management
Tests run automatically before each commit via a git hook. To set it up manually:
# The hook is already configured in .husky/pre-commit
npm run testYour SSE server should respond with the following format:
id: 1
event: message
data: {"your": "json", "data": "here"}
Example Node.js server using better-sse:
import express from 'express'
import { createSession } from 'better-sse'
const app = express()
app.use(express.json())
app.post('/api/events', async (req, res) => {
const session = await createSession(req, res)
let counter = 0
const interval = setInterval(() => {
if (!session.isConnected) {
clearInterval(interval)
return
}
counter++
session.push({ time: new Date(), counter }, 'message', String(counter))
}, 1000)
session.on('disconnected', () => clearInterval(interval))
})
app.listen(8001)MIT © CYB3RL1F3