Have you ever wanted to stream data to your ReactJS app in real time? Maybe show a popup message or shut down the app completely without waiting for a polling solution?
As it turns out, the solution existed since around 2010 and is now fully supported in all major browsers.
Let’s explore Server-Sent Events (SSE), compare them to other real-time data solutions, and implement a robust example using a modern ReactJS app created with Vite.
Overview of Real-Time Data Solutions
When talking about real-time data solutions for ReactJS (or any other JS application), we have a few options:
- Polling (not really real-time)
- Long-Polling
- WebSockets
- Server-Sent Events (SSE)
Here’s how they compare:
Feature/Criteria Breakdown
1. Real-Time
- Polling: No (interval delay)
- Long-Polling: Yes (semi-real-time)
- WebSockets: Yes (true real-time)
- Server-Sent Events (SSE): Yes (true real-time)
2. Ease of Implementation
- Polling: Yes (simple)
- Long-Polling: Yes (moderately simple)
- WebSockets: No (requires setup)
- Server-Sent Events (SSE): Yes (built-in browser support)
3. Browser Support
- Polling: Yes (all browsers)
- Long-Polling: Yes (all browsers)
- WebSockets: Yes (modern browsers)
- Server-Sent Events (SSE): Yes (modern browsers)
4. Efficiency
- Polling: No (constant requests)
- Long-Polling: No (open connections)
- WebSockets: Yes (optimized)
- Server-Sent Events (SSE): Yes (low overhead)
5. Bi-Directional Communication
- Polling: No (client-to-server only)
- Long-Polling: No (client-to-server only)
- WebSockets: Yes (full-duplex)
- Server-Sent Events (SSE): No (server-to-client only)
6. Reconnection Handling
- Polling: No (manual logic)
- Long-Polling: Yes (built-in retry)
- WebSockets: No (manual logic)
- Server-Sent Events (SSE): Yes (auto-reconnect)
7. Use Case Examples
- Polling: Periodic updates (e.g., refreshing feeds)
- Long-Polling: Semi-real-time notifications
- WebSockets: Chat apps, live games
- Server-Sent Events (SSE): Real-time notifications, stock tickers
If your use case involves getting events from the server only, SSE is a simple, efficient, and reliable solution.
Backend Implementation
We’ll use an Express.js server for the backend. Follow these steps:
Basic Express Setup
Start with a basic Express server:
const express = require("express");
const app = express();
const PORT = 3010;
app.use(express.json());
app.get("/events", (req, res) => {
res.status(200).send("Event route is working");
});
app.listen(PORT, () => {
console.log(`SSE server running on http://localhost:${PORT}`);
});
Adding SSE-Specific Headers
Add headers to enable SSE:
app.get("/events", (req, res) => {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
res.write(`data: Connected to SSE\n\n`);
});
Sending Periodic Messages
Send updates periodically to connected clients:
app.get("/events", (req, res) => {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
res.write(`data: Connected to SSE\n\n`);
const interval = setInterval(() => {
const message = { time: new Date().toISOString() };
res.write(`data: ${JSON.stringify(message)}\n\n`);
}, 2000);
req.on("close", () => {
clearInterval(interval);
res.end();
});
});
Enabling CORS
To avoid cross-origin issues, add:
const cors = require("cors");
app.use(cors());
Frontend Implementation Using Vite and ReactJS
Setting Up the Project
Create a new Vite project with ReactJS and TypeScript:
npm create vite@latest sse -- --template react-ts
cd sse
npm install
Setting Up the Component
Edit the App.tsx file to include useEffect and useState hooks:
import { useEffect, useState } from "react";
import "./App.css";
function App() {
const [messages, setMessages] = useState("");
useEffect(() => {
const eventSource = new EventSource("http://localhost:3010/events");
eventSource.onmessage = (event) => {
console.log("Received event:", event.data);
setMessages(event.data);
};
eventSource.onerror = (error) => {
console.error("EventSource failed:", error);
};
return () => {
eventSource.close();
};
}, []);
return (
<div>
<h1>Real-Time Messages</h1>
<p>{messages}</p>
</div>
);
}
export default App;
Advanced Topics
Security Considerations
- Authentication: Secure your SSE endpoint using tokens:
app.get('/events', authenticate, (req, res) => { ... });
- Rate Limiting: Use middleware like
express-rate-limit
to prevent abuse.
Scaling SSE
- Load Balancers: Use NGINX or HAProxy to efficiently manage SSE connections.
- Connection Limits: Implement logic to cap active connections for better scalability.
Debugging SSE
- Use
curl
to test your endpoint:
curl http://localhost:3010/events
- Monitor browser DevTools for network activity and messages.
Custom Event Types
SSE supports custom event types using the event:
keyword:
res.write(`event: customEvent\n`);
res.write(`data: {"info": "custom event triggered"}\n\n`);
Handle them on the frontend:
eventSource.addEventListener("customEvent", (event) => {
console.log("Custom Event:", event.data);
});
Conclusion
With Vite and ReactJS as the frontend framework and a lightweight Express backend, we’ve implemented a robust, scalable real-time solution using Server-Sent Events. The combination of simplicity, efficiency, and modern browser support makes SSE an excellent choice for unidirectional real-time data delivery.
Originally published on DEV.to