Question
WebSockets vs Server-Sent Events (EventSource): Differences, Use Cases, and When to Choose Each
Question
Both WebSockets and Server-Sent Events (SSE, via EventSource) can push data from a server to a browser. They appear to solve similar problems.
What are the practical differences between WebSockets and Server-Sent Events?
When should you choose one over the other?
Please focus on factual, technical differences such as communication model, protocol behavior, browser support considerations, and common use cases.
Short Answer
By the end of this page, you will understand how WebSockets and Server-Sent Events (SSE) differ, what problem each one solves best, and how to choose the right option for real applications. You will also see simple JavaScript examples, common mistakes, and a small project that demonstrates server-to-browser streaming with SSE.
Concept
WebSockets and Server-Sent Events are both used for real-time communication between a browser and a server, but they are not the same tool.
WebSockets
WebSockets provide a full-duplex connection. That means:
- the client can send messages to the server
- the server can send messages to the client
- both directions can happen over the same long-lived connection
A WebSocket connection begins as an HTTP request and then upgrades to the WebSocket protocol. After that upgrade, communication is no longer normal HTTP request/response.
This makes WebSockets a strong choice for features like:
- chat apps
- multiplayer games
- collaborative editing
- live dashboards where clients also send frequent updates
Server-Sent Events (SSE)
SSE is designed for one-way streaming from server to browser over regular HTTP. The browser opens a connection using EventSource, and the server keeps sending text-based events over time.
With SSE:
- the server can push updates to the browser
- the browser does not send messages back through the same SSE connection
- if the browser needs to send data, it uses normal HTTP requests such as
fetch()or form submissions
SSE is often a good fit for:
- notifications
- live news feeds
- status updates
- log streaming
- progress updates
Why this matters
Choosing the wrong tool can add complexity.
- If you only need server → browser updates, SSE is often simpler.
- If you need two-way low-latency messaging, WebSockets are usually the better choice.
Key factual difference
The biggest conceptual difference is:
- WebSockets: two-way persistent messaging
- SSE: one-way server-to-client event stream
That single difference influences architecture, error handling, scaling, and implementation details in real systems.
Mental Model
Think of these two technologies like communication channels:
- WebSocket is like a phone call. Both sides can talk at any time.
- SSE is like a radio broadcast. The server keeps talking, and the browser listens.
If your app only needs to listen for updates, a radio broadcast is simpler. If your app needs a conversation, you need the phone call.
Syntax and Examples
WebSocket in JavaScript
const socket = new WebSocket('ws://localhost:3000');
socket.addEventListener('open', () => {
console.log('Connected');
socket.send('Hello server');
});
socket.addEventListener('message', (event) => {
console.log('Received:', event.data);
});
socket.addEventListener('close', () => {
console.log('Connection closed');
});
This creates a persistent two-way connection. The browser can both send and receive messages.
Server-Sent Events in JavaScript
const source = new EventSource('/events');
source.addEventListener('message', (event) => {
console.log('Update:', event.data);
});
source.addEventListener('error', (error) => {
console.error('SSE error:', error);
});
This opens a one-way stream from the server to the browser. The browser listens for updates, but does not send messages through EventSource.
Example: sending data back when using SSE
const source = new EventSource('/events');
source.onmessage = (event) => {
console.log('Server says:', event.data);
};
async function sendPreference() {
await fetch('/preferences', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ theme: 'dark' })
});
}
With SSE, sending data back usually happens through separate HTTP requests.
Minimal SSE server format
An SSE response must be sent with the correct content type and event format:
data: Hello client
Each event ends with a blank line. The data: prefix is required for standard message data.
Step by Step Execution
Consider this SSE example:
const source = new EventSource('/events');
source.onmessage = (event) => {
console.log('Message from server:', event.data);
};
Here is what happens step by step:
-
new EventSource('/events')tells the browser to open an HTTP connection to/events. -
The server responds with headers such as:
Content-Type: text/event-stream Cache-Control: no-cache Connection: keep-alive -
The server keeps the connection open instead of ending the response.
-
When the server writes:
data: Build completethe browser receives one SSE message.
-
The browser triggers
source.onmessage. -
event.datacontains the stringBuild complete. -
If the connection drops, the browser will usually try to reconnect automatically.
Now compare that with WebSocket:
const socket = new WebSocket('ws://localhost:3000');
socket.onopen = () => {
socket.send('ping');
};
socket.onmessage = (event) => {
console.log(event.data);
};
Step by step:
- The browser starts with an HTTP request that asks to upgrade the connection.
- If the server accepts, the protocol switches to WebSocket.
- The browser gets the
openevent. - The client sends
pingover the socket. - The server can reply with a message.
- The browser receives the message in
onmessage. - Either side can continue sending more messages until the connection closes.
The important difference is that SSE is a stream of server events, while WebSocket is an ongoing conversation.
Real World Use Cases
Good use cases for SSE
SSE is usually a strong choice when updates only need to flow from the server to the browser:
- Notifications: new alerts, messages, or reminders
- Live logs: deployment logs, job output, monitoring streams
- Status updates: background task progress, queue progress, report generation
- News or price tickers: simple feed-style updates
- Admin dashboards: server health, counts, or activity feeds
Good use cases for WebSockets
WebSockets are better when both sides need to send data frequently or with low latency:
- Chat applications
- Multiplayer games
- Collaborative editors
- Real-time whiteboards
- Trading interfaces where the client also sends actions quickly
- Remote control systems where commands and updates happen continuously
Rule of thumb
- Choose SSE for simple server push.
- Choose WebSocket for interactive two-way communication.
Real Codebase Usage
In real projects, developers usually choose based on communication direction, infrastructure, and simplicity.
Common SSE patterns
- Streaming background job progress
- Pushing notifications after database changes
- Sending log lines to an admin page
- Keeping implementation simple using standard HTTP
A common pattern is:
- Browser opens an
EventSource - Server emits updates as events
- Browser sends user actions back separately with
fetch()
This works well when the app does not need a custom bidirectional protocol.
Common WebSocket patterns
- Message hubs for chat rooms
- Publish/subscribe systems for real-time channels
- Presence tracking such as online/offline users
- Low-latency event handling for collaborative apps
Related codebase patterns
Guard clauses
When handling incoming WebSocket messages, code often validates the message early:
function handleMessage(raw) {
let message;
try {
message = JSON.parse(raw);
} catch {
return;
}
if (!message.type) return;
// process valid message
}
Validation
WebSocket servers often validate every incoming message because clients can send arbitrary data.
SSE usually reduces this complexity because the browser is only consuming events.
Error handling
- SSE often relies on built-in reconnection behavior.
- WebSocket apps often implement custom reconnect logic, heartbeat checks, and retry backoff.
Configuration
In production, teams also consider:
- reverse proxy support
- timeouts
- load balancer behavior
- connection limits
- message format choices such as plain text or JSON
Common Mistakes
1. Using WebSockets when SSE is enough
If the browser only needs to receive updates, WebSockets may add unnecessary complexity.
Better choice: use SSE for one-way event streams.
2. Expecting SSE to send client messages
This is a very common misunderstanding.
Broken expectation:
const source = new EventSource('/events');
source.send('hello'); // ❌ EventSource does not have send()
EventSource is receive-only.
3. Forgetting the correct SSE response format
Broken SSE output:
Hello client
Correct SSE output:
data: Hello client
The data: field and blank line are important.
4. Assuming WebSockets are just faster HTTP
WebSockets are a different protocol after the upgrade. They are not the same as repeatedly calling fetch().
5. Ignoring reconnection behavior
SSE clients often reconnect automatically. Beginners sometimes forget this and accidentally duplicate state handling.
6. Not handling connection close events in WebSockets
Broken example:
const socket = new WebSocket('ws://localhost:3000');
socket.onmessage = (event) => console.log(event.data);
This receives data, but has no logic for disconnects, retries, or errors.
7. Sending the wrong data format
For WebSockets, teams often send JSON. If you expect JSON, parse and validate it carefully.
socket.onmessage = (event) => {
const data = JSON.parse(event.data); // can throw if invalid
console.log(data);
};
Safer version:
socket.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
console.log(data);
} catch (error) {
console.error('Invalid JSON:', error);
}
};
Comparisons
| Feature | WebSockets | Server-Sent Events (SSE) |
|---|---|---|
| Communication direction | Two-way | Server to client only |
| Protocol | WebSocket protocol after HTTP upgrade | Standard HTTP streaming |
| Browser API | WebSocket | EventSource |
| Client can send messages on same connection | Yes | No |
| Server can push updates | Yes | Yes |
| Message format | Binary or text | Text-based event stream |
| Auto-reconnect support | Usually custom | Typically built in by browser |
| Simplicity | More complex | Simpler for one-way updates |
| Good for chat/collaboration | Yes | Usually no |
| Good for notifications/log streams | Can work | Excellent fit |
SSE vs polling
| Approach | How it works | Best for |
|---|---|---|
| Polling | Browser repeatedly asks for updates | Simple systems with low update frequency |
| SSE | Server pushes updates over one open HTTP response | Real-time server-to-client updates |
| WebSocket | Persistent two-way channel | Interactive real-time apps |
Which should you choose?
-
Use SSE if:
- updates flow mostly from server to browser
- you want simpler implementation
- text-based event streams are enough
-
Use WebSockets if:
- both sides need to send frequent messages
- low-latency interaction matters
- your app behaves more like a conversation than a feed
Cheat Sheet
Quick decision
- Need server → browser only? Use SSE.
- Need browser ↔ server both ways? Use WebSocket.
WebSocket basics
const socket = new WebSocket('ws://localhost:3000');
socket.send('hello');
socket.onmessage = (event) => console.log(event.data);
- Persistent two-way connection
- Supports text and binary data
- Requires connection upgrade
- Usually needs custom reconnect logic
SSE basics
const source = new EventSource('/events');
source.onmessage = (event) => console.log(event.data);
- One-way server-to-client stream
- Built on HTTP
- Text-based only
- Browser usually handles reconnection automatically
SSE event format
data: some message
Optional fields can include:
event: status
data: done
id: 42
Common rules
EventSourcecannot send data back- WebSockets can send and receive on the same connection
- SSE is often easier for notifications, logs, and progress updates
- WebSockets are better for chat, collaboration, and game state
Watch out for
- connection timeouts in proxies
- reconnection behavior
- duplicate event handling after reconnect
- validating incoming WebSocket messages
FAQ
What is the main difference between WebSockets and SSE?
WebSockets support two-way communication, while SSE only sends events from the server to the browser.
Is SSE faster than WebSockets?
Not in a universal sense. SSE can be simpler and efficient for one-way updates, while WebSockets are better suited for continuous two-way messaging.
Can Server-Sent Events replace WebSockets?
Only when your app needs server-to-client updates only. If the client must send frequent real-time messages, SSE is not a full replacement.
Can I send data from the browser when using SSE?
Yes, but not through the EventSource connection. You usually send data with fetch() or another HTTP request.
Are WebSockets over HTTP?
A WebSocket connection starts with HTTP, then upgrades to the WebSocket protocol.
Do Server-Sent Events reconnect automatically?
In many cases, yes. Browsers typically try to reconnect automatically when the stream is interrupted.
Should I use SSE for chat applications?
Usually no. Chat needs two-way communication, so WebSockets are generally a better fit.
Is SSE text-only?
Yes, SSE sends text-based event data. If needed, structured data is commonly sent as JSON strings.
Mini Project
Description
Build a small real-time status feed where the browser receives live updates from the server using Server-Sent Events. This demonstrates when SSE is a good fit: the server pushes information continuously, and the browser only needs to listen.
Goal
Create a page that shows a stream of server-generated messages in real time using EventSource.
Requirements
- Create an HTTP endpoint that returns an SSE stream.
- Send a new message from the server every few seconds.
- Create a browser page that connects with
EventSource. - Display each incoming message in a list on the page.
- Handle connection errors in the browser.
Keep learning
Related questions
Add a Favicon to a Static HTML Page
Learn how to add a favicon to static HTML pages, use correct paths and icon formats, and troubleshoot browser caching and local-file issues.
Allow Only Numeric Input (0-9) in HTML Input Using jQuery
Learn how to allow only digits 0-9 in an HTML input using jQuery, with examples, validation tips, common mistakes, and best practices.
Apply CSS !important with jQuery .css()
Learn why jQuery .css() cannot set !important and how to use CSSStyleDeclaration.setProperty() to apply inline important styles safely.