Loading...
Loading...
Real-time communication. WebSocket, message flow, and handling connectivity.
Module 11 · JavaScript Projects
Theory becomes skill through building. These guided projects — calculator, todo app, weather app, and more — combine everything you've learned into real applications you can extend, share, and put in a portfolio.
Learn Chat App in JavaScript — covering WebSocket Basics, Message Flow, UI Rendering, Features with interactive visual examples and step-by-step explanations.
Reading about JavaScript is not the same as building with it. These guided projects combine variables, functions, DOM manipulation, async fetch, and state management into applications you can run, extend, and showcase.
Each project walks through architecture decisions, common pitfalls, and incremental steps. By the end you will have portfolio-ready apps and the confidence to start your own projects from scratch.
Understanding chat app is essential for every JavaScript developer. It shows up in frontend UI code, backend APIs, and framework internals — and getting it wrong leads to bugs that are hard to trace. This chapter builds intuition with visuals so the behavior sticks.
Persistent two-way connection between client and server.
// WebSocket vs HTTP: // HTTP: Client asks → Server responds → connection closes // WebSocket: Connection stays open → both sides send anytime // Client-side WebSocket: const socket = new WebSocket("wss://chat-server.example.com"); socket.addEventListener("open", () => { console.log("Connected to server"); socket.send(JSON.stringify({ type: "join", username: "Alice", })); }); socket.addEventListener("message", (event) => { const data = JSON.parse(event.data); handleServerMessage(data); }); socket.addEventListener("close", (event) => { console.log("Disconnected:", event.code, event.reason); // Attempt reconnection... }); socket.addEventListener("error", (event) => { console.error("WebSocket error:", event); }); // Server-side (Node.js + ws library): // const WebSocket = require("ws"); // const wss = new WebSocket.Server({ port: 8080 }); // // wss.on("connection", (ws) => { // ws.on("message", (data) => { // const msg = JSON.parse(data); // // Broadcast to all clients: // wss.clients.forEach(client => { // if (client.readyState === WebSocket.OPEN) { // client.send(data); // } // }); // }); // });
Structured message protocol between client and server.
// Message protocol — every message has a "type": // Client → Server: // { type: "join", username: "Alice" } // { type: "message", text: "Hello!" } // { type: "typing", isTyping: true } // { type: "leave" } // Server → Client: // { type: "message", id: "uuid", username: "Bob", text: "Hi!", timestamp: ... } // { type: "user-joined", username: "Alice", onlineCount: 5 } // { type: "user-left", username: "Alice", onlineCount: 4 } // { type: "typing", username: "Bob", isTyping: true } // { type: "history", messages: [...] } // Send a chat message: function sendMessage(text) { if (!text.trim() || socket.readyState !== WebSocket.OPEN) return; const message = { type: "message", text: text.trim(), timestamp: Date.now(), }; socket.send(JSON.stringify(message)); // Optimistic UI — show immediately: appendMessage({ ...message, username: currentUser, isMine: true }); }
Define a clear protocol: every message is JSON with a 'type' field. The server routes based on type.
Chat bubbles, timestamps, and auto-scroll.
// Chat UI structure: // <div class="chat-app"> // <header> // <h1>Chat Room</h1> // <span class="online-count">3 online</span> // </header> // <div class="messages"></div> // <div class="typing-indicator hidden"></div> // <form class="message-form"> // <input type="text" placeholder="Type a message..."> // <button type="submit">Send</button> // </form> // </div> const messagesEl = document.querySelector(".messages"); function appendMessage(msg, shouldScroll = true) { const isMine = msg.username === currentUser; const time = formatTime(msg.timestamp); const messageEl = document.createElement("div"); messageEl.className = `message ${isMine ? "mine" : "theirs"}`; messageEl.innerHTML = ` ${!isMine ? `<span class="username">${escapeHtml(msg.username)}</span>` : ""} <div class="bubble"> <p>${escapeHtml(msg.text)}</p> <time>${time}</time> </div> `; messagesEl.appendChild(messageEl); if (shouldScroll) scrollToBottom(); } function appendSystemMessage(text) { const el = document.createElement("div"); el.className = "system-message"; el.textContent = text; messagesEl.appendChild(el); scrollToBottom(); } // Smart auto-scroll: only scroll if user is near bottom: function scrollToBottom() { const { scrollTop, scrollHeight, clientHeight } = messagesEl; const isNearBottom = scrollHeight - scrollTop - clientHeight < 100; if (isNearBottom) { messagesEl.scrollTop = scrollHeight; } } function formatTime(timestamp) { return new Date(timestamp).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", }); }
Typing indicators, online status, and notifications.
// Typing indicator: const typingEl = document.querySelector(".typing-indicator"); const typingUsers = new Set(); // Send typing status (debounced): let typingTimeout; input.addEventListener("input", () => { socket.send(JSON.stringify({ type: "typing", isTyping: true })); clearTimeout(typingTimeout); typingTimeout = setTimeout(() => { socket.send(JSON.stringify({ type: "typing", isTyping: false })); }, 2000); // stop after 2s of no typing }); // Display who's typing: function updateTypingIndicator(username, isTyping) { if (isTyping) typingUsers.add(username); else typingUsers.delete(username); if (typingUsers.size === 0) { typingEl.classList.add("hidden"); } else { typingEl.classList.remove("hidden"); const users = [...typingUsers]; if (users.length === 1) { typingEl.textContent = `${users[0]} is typing...`; } else { typingEl.textContent = `${users.length} people are typing...`; } } } // Browser notifications for new messages: async function requestNotificationPermission() { if (!("Notification" in window)) return; if (Notification.permission === "default") { await Notification.requestPermission(); } } function notifyNewMessage(msg) { if (document.hasFocus()) return; // don't notify if app is active if (Notification.permission !== "granted") return; new Notification(msg.username + " says:", { body: msg.text.slice(0, 100), icon: "/chat-icon.png", tag: "chat-message", // replace previous notification }); } // Online users list: function updateOnlineCount(count) { document.querySelector(".online-count").textContent = `${count} online`; }
Handle disconnections, reconnect automatically.
// Auto-reconnect with exponential backoff: class ChatConnection { constructor(url, username) { this.url = url; this.username = username; this.socket = null; this.retryCount = 0; this.maxRetries = 5; this.messageQueue = []; // buffer messages while disconnected } connect() { this.socket = new WebSocket(this.url); this.socket.onopen = () => { this.retryCount = 0; updateStatus("connected"); // Send buffered messages: while (this.messageQueue.length > 0) { this.socket.send(this.messageQueue.shift()); } // Re-join: this.send({ type: "join", username: this.username }); }; this.socket.onclose = () => { updateStatus("disconnected"); this.scheduleReconnect(); }; this.socket.onmessage = (e) => handleServerMessage(JSON.parse(e.data)); this.socket.onerror = () => updateStatus("error"); } send(data) { const json = JSON.stringify(data); if (this.socket?.readyState === WebSocket.OPEN) { this.socket.send(json); } else { this.messageQueue.push(json); // buffer for later } } scheduleReconnect() { if (this.retryCount >= this.maxRetries) { updateStatus("failed"); return; } const delay = Math.min(1000 * Math.pow(2, this.retryCount), 30000); this.retryCount++; updateStatus(`reconnecting in ${delay / 1000}s...`); setTimeout(() => this.connect(), delay); } disconnect() { this.maxRetries = 0; // prevent reconnect this.socket?.close(); } } // Usage: const chat = new ChatConnection("wss://server.example.com", "Alice"); chat.connect(); // Key patterns in this project: // ✓ WebSocket real-time communication // ✓ Message protocol design // ✓ Optimistic UI updates // ✓ Auto-reconnect with backoff // ✓ Message buffering during disconnect // ✓ Typing indicators with debounce // ✓ Browser Notifications API // ✓ Smart auto-scroll
Common questions about chat app.