Build a Real-Time Chat App With WebSockets
SkillVeris Team
Engineering Team

A real-time chat app uses WebSockets — a persistent, two-way connection — so the server can push messages to clients the instant they arrive.
In this guide, you'll learn:
- Unlike HTTP request-response, a WebSocket stays open, letting either side send data at any time with low overhead.
- The server keeps a set of connected clients and broadcasts each incoming message to all of them.
- The browser's built-in WebSocket API connects with new WebSocket(url) and fires onmessage on incoming data.
- Rooms, usernames, and presence are application logic layered on top of the raw socket.
1What Makes Chat Real-Time
A real-time chat app delivers messages the moment they are sent, with no page refresh, by keeping a persistent WebSocket connection open between each client and the server. When one user sends a message, the server pushes it to every connected client immediately.
This is fundamentally different from traditional HTTP, where the client must ask before the server can answer. WebSockets flip that model: after an initial handshake, the connection stays open and either side can send data whenever it wants. That bidirectional, always-on channel is what makes instant chat, live notifications, and collaborative apps possible.
2WebSockets vs HTTP
Understanding why WebSockets exist clarifies the whole project. Plain HTTP is request-response: the client asks, the server replies, the connection closes. To fake real-time with HTTP you would have to poll repeatedly, wasting bandwidth and adding delay.
A WebSocket starts as an HTTP request with an Upgrade header, then switches protocols and keeps the socket open. From then on, messages flow both ways with minimal overhead — no repeated headers, no reconnecting for each message.
- HTTP: client must initiate every exchange; the server cannot push.
- Polling: repeated HTTP requests to simulate updates — wasteful and laggy.
- WebSocket: one persistent connection; either side sends at any time.
- Upgrade handshake: the connection begins as HTTP, then switches protocols.
- Low overhead: no per-message headers once the socket is open.
🔑Key Idea
WebSockets turn the server from a passive responder into an active pusher. That single capability is what separates a real-time app from one that constantly polls for updates.
3Building the Server
The server's job is to accept connections, keep track of who is connected, and relay messages. A common stack is Node.js with the ws library, or Python with the websockets package. The core pattern is identical: maintain a collection of active connections and broadcast to all of them.
When a client connects, add its socket to the set. When it sends a message, loop over the set and forward the message to everyone. When it disconnects, remove it. That is the entire backbone of a chat server.
A Minimal Node Server With ws
Track clients in a Set and broadcast each incoming message to all of them.
const { WebSocketServer } = require('ws');
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', (ws) => {
ws.on('message', (data) => {
for (const client of wss.clients)
if (client.readyState === 1) client.send(data.toString());
});
});4Connecting From the Browser
The browser ships with a built-in WebSocket API, so no library is required on the client. Create a connection with new WebSocket(url), then attach handlers for the events you care about: onopen, onmessage, onclose, and onerror.
Sending is just socket.send(text), and receiving fires onmessage with the data. Render each incoming message into the chat window, and send the input field's contents when the user hits enter.
- const socket = new WebSocket('ws://localhost:8080');
- socket.onmessage = (event) => addMessage(event.data);
- form.onsubmit = (e) => {
- e.preventDefault();
- socket.send(input.value);
- input.value = '';
- };
5Message Format, Usernames, and Rooms
Raw text works for a demo, but real apps send structured messages. Send JSON with a type, a username, the text, and a timestamp so the client can render richer UI and the server can route intelligently.
Rooms are just an added field: the server groups connections by room name and only broadcasts a message to sockets in the same room. Usernames and presence (who is online) are likewise application logic you layer on top of the raw socket — the WebSocket itself only carries bytes.
Structured Messages
JSON payloads let you carry metadata alongside the text.
socket.send(JSON.stringify({ type: 'chat', room: 'general', user: name, text }));
// server parses, checks msg.room, broadcasts to that room only6Reconnection and Reliability
Persistent connections drop — networks hiccup, laptops sleep, servers restart. A robust chat client detects a close and reconnects automatically, ideally with a growing delay (exponential backoff) so a downed server is not hammered by every client at once.
On the server side, clean up disconnected sockets so your client set does not fill with dead connections, and consider a periodic ping to detect clients that vanished without a proper close.
- socket.onclose = () => setTimeout(connect, backoff);
- Increase the backoff delay after each failed attempt, capped at a maximum.
- Reset the delay once a connection succeeds.
- On the server, remove sockets on close and ping to detect stale ones.
- Queue outgoing messages while disconnected, then flush on reconnect.
7Common Mistakes to Avoid
Real-time apps introduce failure modes that request-response apps never face.
- Not handling disconnects, so the app silently stops receiving messages.
- Trusting client input — always validate and sanitize before broadcasting.
- Broadcasting to sockets that are closing; check readyState first.
- Forgetting to remove disconnected clients, leaking memory over time.
- Using ws:// in production instead of the encrypted wss:// over TLS.
⚠️Watch Out
Any message a client sends can reach every other client. Validate and sanitize on the server before broadcasting, or one user can inject scripts or spam into everyone's chat window.
8Key Takeaways
Real-time chat is a clear window into event-driven networking.
- WebSockets keep a persistent, two-way connection so the server can push instantly.
- The server tracks connected clients and broadcasts each message to them.
- The browser's built-in WebSocket API needs no client library.
- Send JSON to carry usernames, rooms, and timestamps alongside text.
- Handle reconnection with backoff and validate every message server-side.
9Frequently Asked Questions
Q: What is the difference between WebSockets and HTTP? A: HTTP is request-response — the client must ask before the server can reply. A WebSocket keeps a persistent, bidirectional connection open after an initial handshake, so either side can send data at any time. That is why WebSockets suit real-time chat and HTTP does not.
Q: Do I need a library on the client? A: No. Browsers include a native WebSocket API, so new WebSocket(url) works out of the box. Libraries like Socket.IO add features such as automatic reconnection and fallbacks, but they are optional.
Q: How do I handle a dropped connection? A: Listen for the onclose event and reconnect after a delay, increasing the delay with exponential backoff. On the server, remove closed sockets and use pings to detect clients that disappeared without closing cleanly.
Q: Should I use ws:// or wss://? A: Use wss:// in production. It is WebSocket over TLS, encrypting traffic just as HTTPS does for HTTP. Plain ws:// is fine only for local development.
Get The Print Version
Download a PDF of this article for offline reading.
About the Publisher
SkillVeris Team
Engineering Team
Our engineering team documents real build journeys so you can learn by doing, not just reading.
View all postsRelated Posts
Never miss an update
Get the latest tutorials and guides delivered to your inbox.
No spam. Unsubscribe anytime.