WebSockets
Real-time bidirectional communication between browser and server — opening connections, sending messages, handling events, and reconnecting.
new WebSocket(url) returns immediately, but the connection isn't open yet: the handshake still has to happen over the network. Calling send() right away throws InvalidStateError: Still in CONNECTING state. It's an easy mistake when the first message ("join this room") is written straight after the constructor, as in the sending examples above.
const ws = new WebSocket("ws://localhost:8765/ws");
ws.onmessage = (e) => console.log(e.data);
ws.send(JSON.stringify({ type: "join", room: "general" })); // right after creating it
// InvalidStateError: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.Staying connected — real-time data from the server
HTTP works like sending a letter: you send a request, the server replies, done. A WebSocket is like a phone call: the connection stays open, and both sides can talk whenever they want. This is how chat apps, live stock prices, multiplayer games, and collaborative tools work.
Opening a WebSocket connection
// wss:// = secure WebSocket (like https)
// ws:// = insecure (use only in development/localhost)
const ws = new WebSocket("wss://api.example.com/ws");
// Events — what can happen to the connection:
ws.onopen = () => {
console.log("Connected to server!");
ws.send("Hello server!"); // send as soon as connected
};
ws.onmessage = (event) => {
// event.data = the message from the server (string or Blob)
console.log("Server says:", event.data);
};
ws.onerror = (error) => {
console.error("Connection error");
};
ws.onclose = (event) => {
console.log("Disconnected. Code:", event.code);
};Sending and receiving messages
// Send a plain text message:
ws.send("Hello!");
// Send JSON (the most common pattern):
ws.send(JSON.stringify({
type: "chat",
room: "general",
message: "Hi everyone!",
timestamp: Date.now(),
}));
// Receive and parse JSON:
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log("Message type:", data.type);
if (data.type === "chat") {
displayMessage(data.message, data.from);
} else if (data.type === "users") {
updateUserList(data.users);
}
};Send only when readyState is OPEN, and queue anything sent earlier so it goes out from onopen. The ChatRoom.send() below checks readyState but then silently drops early messages. A queue keeps them instead:
const ws = new WebSocket("ws://localhost:8765/ws");
const outbox = [];
function send(msg) {
const data = JSON.stringify(msg);
if (ws.readyState === WebSocket.OPEN) ws.send(data);
else outbox.push(data); // not open yet: queue it
}
ws.onopen = () => {
while (outbox.length) ws.send(outbox.shift()); // flush the queue once connected
};
ws.onmessage = (e) => console.log(e.data);
send({ type: "join", room: "general" });
// echo: {"type":"join","room":"general"} (from a local echo server)A simple chat example
class ChatRoom {
constructor(url) {
this.ws = new WebSocket(url);
this.setupHandlers();
}
setupHandlers() {
this.ws.onopen = () => console.log("Joined chat");
this.ws.onmessage = (e) => this.handleMessage(JSON.parse(e.data));
this.ws.onclose = () => console.log("Left chat");
}
handleMessage(data) {
switch (data.type) {
case "message": displayChat(data.user, data.text); break;
case "join": showNotification(`${data.user} joined`); break;
case "leave": showNotification(`${data.user} left`); break;
}
}
send(text) {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type: "message", text }));
}
}
leave() { this.ws.close(1000, "User left"); }
}Official Sources
- MDN Web Docs — WebSocket
- MDN Web Docs — Writing WebSocket client applications
- RFC 6455 — The WebSocket Protocol
- MDN Web Docs — Server-sent events
The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.