This content originally appeared on DEV Community and was authored by Serif COLAKEL
A Complete Developerβs Guide with Node.js, React, React Native, and Next.js
1. What is Server-Sent Events (SSE)?
Server-Sent Events (SSE) is a unidirectional streaming protocol built into modern browsers. It allows servers to push real-time updates to the client over a single long-lived HTTP connection using the EventSource API.
Think of it as a reverse
fetchβonce initiated, the server keeps sending data as events happen.
Key Characteristics
- Built on HTTP/1.1 (no WebSocket upgrade needed)
- Text/event-stream content type
- Auto-reconnect mechanism by the browser
- Low overhead, lightweight, easy to implement
2. When to Use SSE?
SSE shines in server-to-client real-time delivery scenarios, especially where simplicity and HTTP compatibility are key.
Common Use Cases
| Use Case | Why SSE Fits Well |
|---|---|
Notifications |
Push updates to logged-in users instantly |
Live Dashboards |
Periodic updates to analytics or KPIs |
Real-Time News Feeds |
Auto-refreshing breaking news |
Tickers & Clocks |
Update countdowns or current time |
IoT Device Updates |
Sensor streams without bidirectional need |
Use WebSocket when you require two-way communication (e.g. chat, multiplayer games).
3. How SSE Works
Server Response
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
Each message sent must:
- Start with
data:and end with two newlines - Optionally include:
id:,event:,retry:
Message Format
data: {"message":"Hello, client!"}
data: {"time":"2025-08-02T12:00:00Z"}
Basic JavaScript Client
const es = new EventSource("http://localhost:4000/events");
es.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log("Received:", data);
};
es.onerror = () => {
console.warn("Connection lost. Reconnecting...");
};
4. Node.js Express SSE Server Example
const express = require("express");
const cors = require("cors");
const app = express();
app.use(cors());
app.get("/events", (req, res) => {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
const send = () => {
const payload = JSON.stringify({ time: new Date().toISOString() });
res.write(`data: ${payload}\n\n`);
};
send(); // Send initial data immediately
const interval = setInterval(send, 5000);
req.on("close", () => {
clearInterval(interval);
console.log("Cleanup: Client disconnected");
});
});
app.listen(4000, () => console.log("SSE server running on port 4000"));
Tip: Add
res.flush()if usingcompressionmiddleware (to push chunks immediately).
5. SSE Clients
React (Web)
import { useEffect, useState } from "react";
export default function SSEComponent() {
const [time, setTime] = useState("");
useEffect(() => {
const es = new EventSource("http://localhost:4000/events");
es.onmessage = (e) => {
const data = JSON.parse(e.data);
setTime(data.time);
};
es.onerror = () => {
console.warn("SSE connection error");
};
return () => es.close();
}, []);
return <div>Server Time: {time}</div>;
}
React Native (Mobile)
npm install react-native-event-source
Alternative Packages:
- https://www.npmjs.com/package/react-native-sse
- https://www.npmjs.com/package/@ammarahmed/react-native-eventsource
import EventSource from "react-native-event-source";
import { useEffect } from "react";
export default function LiveStream() {
useEffect(() => {
const es = new EventSource("http://192.168.1.x:4000/events");
es.onmessage = (event) => {
console.log("Data:", event.data);
};
return () => es.close();
}, []);
return null;
}
Use IP instead of
localhostand ensure both devices are on the same network.
Next.js (Client Component)
"use client";
import { useEffect, useState } from "react";
export default function Page() {
const [data, setData] = useState("");
useEffect(() => {
const es = new EventSource("/api/events");
es.onmessage = (e) => {
setData(JSON.parse(e.data).time);
};
return () => es.close();
}, []);
return <div>Time: {data}</div>;
}
Next.js API Route (SSE Handler)
export function GET(req: Request) {
const encoder = new TextEncoder();
const stream = new ReadableStream({
start(controller) {
const interval = setInterval(() => {
const data = `data: ${JSON.stringify({
time: new Date().toISOString(),
})}\n\n`;
controller.enqueue(encoder.encode(data));
}, 5000);
req.signal.addEventListener("abort", () => {
clearInterval(interval);
controller.close();
});
},
});
return new Response(stream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
},
});
}
6. Advanced SSE Features
Custom Event Types
res.write("event: customEvent\n");
res.write(`data: {"message":"Custom event triggered!"}\n\n`);
es.addEventListener("customEvent", (e) => {
console.log("Custom Event:", e.data);
});
Retry Delay Control
retry: 10000
data: {"message":"Retry in 10s"}
Client waits 10 seconds before reconnecting.
7. Pros and Cons
Pros
- Native browser API (
EventSource) - No third-party library needed
- Simple, scalable (good for thousands of clients)
- Works well with HTTP infrastructure
Cons
- One-way only
- Not supported in all mobile environments (needs polyfill)
- Needs proxy tweaks (e.g., Cloudflare, Nginx)
- No binary or structured message formats (text-only)
8. SSE vs WebSocket
| Feature | SSE | WebSocket |
|---|---|---|
| Direction | Server Client |
Bidirectional |
| Protocol | HTTP/1.1 | WS (Upgrade from HTTP) |
| Browser Support | Most browsers | All modern browsers |
| Complexity | Low | MediumβHigh |
| Streaming | Text-only | Binary, JSON, Text |
| Mobile Support | Polyfill required | Native |
| Reconnect | Built-in basic retry | Manual handling required |
9. Production Considerations
Disable compression middleware to prevent buffered messages
Turn off proxy buffering (e.g., Nginx)
location /events {
proxy_pass http://localhost:4000;
proxy_buffering off;
chunked_transfer_encoding off;
}
Use heartbeats (e.g., data:\n\n) to keep connections alive
Reconnection logic: Use es.onerrorores.readyStatechecks
10. Alternatives
| Tech | Direction | Transport |
|---|---|---|
| WebSocket | Two-way | WebSocket |
| Socket.IO | Two-way + fallback | WebSocket, polling |
| GraphQL Subscriptions | Two-way (GraphQL) | WebSocket |
| Firebase | Two-way | Proprietary |
| Pusher / Ably | Pub/Sub | WebSocket / HTTP |
| Kafka REST Proxy | One-way stream | HTTP/REST |
11. Conclusion
Server-Sent Events are a lightweight, simple, and scalable way to stream real-time data from server to client using nothing but HTTP and vanilla JavaScript.
Theyβre a perfect fit for dashboards, notifications, live feeds, and IoT monitoringβespecially when you donβt need two-way communication.
Use SSE when simplicity and scalability matter. Use WebSocket when interaction matters.
12. Resources
This content originally appeared on DEV Community and was authored by Serif COLAKEL
Notifications
Live Dashboards
Real-Time News Feeds
Tickers & Clocks
IoT Device Updates
Use WebSocket when you require two-way communication (e.g. chat, multiplayer games).
Client
MDN β Server-sent Events
WHATWG Spec
Node.js EventSource Polyfill