Build a Real‑Time Chat App with WebSockets and Node.js
Read this article in clean Markdown format for LLMs and AI context.Ever wanted a simple chat window that works instantly, without refreshing the page? Almost every web app needs some kind of live update – think notifications, live scores, or a quick team chat. This post on CodeCraft shows you how to get a real‑time chat up and running using WebSockets and Node.js. No fancy frameworks, just plain JavaScript and a few npm packages. By the end you’ll have a working app you can run on your laptop and share with a friend.
Why WebSockets?
When you open a normal web page, the browser asks the server for data, gets a response, and then it’s done. If you need new data later you have to ask again – that’s called polling. Polling works, but it’s wasteful: the server gets a lot of empty requests, and the user sees a tiny delay.
WebSockets solve that. They open a single, two‑way connection that stays open. The server can push messages to the client instantly, and the client can send messages back just as fast. Think of it like a telephone line instead of sending letters back and forth.
What You’ll Need
- Node.js (v18 or newer) – the runtime that will host our server.
- npm – comes with Node, used to install packages.
- A text editor – VS Code works fine.
- A terminal – any will do.
We’ll use two npm packages:
- express – a tiny web server to serve our HTML page.
- ws – a lightweight WebSocket library for Node.
Both are tiny, well‑maintained, and perfect for a learning project.
Step 1: Set Up the Project Folder
Open a terminal and run:
mkdir codecraft-chat
cd codecraft-chat
npm init -y
npm install express ws
npm init -y creates a default package.json. The express and ws packages are added to the dependencies list.
Step 2: Write the Server (server.js)
Create a file called server.js and paste the following code:
// server.js
const express = require('express')
const http = require('http')
const WebSocket = require('ws')
// Create an Express app
const app = express()
app.use(express.static('public')) // serve static files from /public
// Create an HTTP server and attach Express
const server = http.createServer(app)
// Create a WebSocket server on top of the HTTP server
const wss = new WebSocket.Server({ server })
// Broadcast a message to all connected clients
function broadcast(data, wsSender) {
wss.clients.forEach(client => {
// Only send to clients that are still open and not the sender
if (client !== wsSender && client.readyState === WebSocket.OPEN) {
client.send(data)
}
})
}
// When a new client connects
wss.on('connection', ws => {
console.log('New client connected')
// When this client sends a message
ws.on('message', message => {
console.log('Received:', message)
// Send the message to everyone else
broadcast(message, ws)
})
// When the client disconnects
ws.on('close', () => {
console.log('Client left')
})
})
// Start the server
const PORT = process.env.PORT || 3000
server.listen(PORT, () => {
console.log(`CodeCraft server listening on http://localhost:${PORT}`)
})
A quick walk‑through:
- We serve static files from a folder called
public. That’s where our HTML, CSS, and client‑side JavaScript will live. - The
wslibrary creates a WebSocket server (wss) that shares the same port as the HTTP server. - When a client sends a message, we call
broadcastto forward it to every other client. This keeps the chat in sync.
Step 3: Build the Front End
Create a folder named public next to server.js. Inside public add three files: index.html, style.css, and chat.js.
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CodeCraft Real‑Time Chat</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>CodeCraft Chat</h1>
<div id="chat-box"></div>
<form id="chat-form">
<input id="msg-input" type="text" placeholder="Type a message…" autocomplete="off" required />
<button type="submit">Send</button>
</form>
<script src="chat.js"></script>
</body>
</html>
A tiny page: a heading, a box to show messages, and a form to type new ones.
style.css
body {
font-family: Arial, sans-serif;
margin: 2rem;
background: #f9f9f9;
}
h1 {
color: #333;
}
#chat-box {
border: 1px solid #ccc;
height: 300px;
overflow-y: auto;
padding: 0.5rem;
background: #fff;
}
.message {
margin: 0.3rem 0;
}
.message.me {
text-align: right;
color: #0066cc;
}
form {
margin-top: 1rem;
display: flex;
}
input {
flex: 1;
padding: 0.5rem;
}
button {
padding: 0.5rem 1rem;
}
Nothing fancy – just enough to make the chat readable.
chat.js
// chat.js
const ws = new WebSocket(`ws://${location.host}`)
const chatBox = document.getElementById('chat-box')
const form = document.getElementById('chat-form')
const input = document.getElementById('msg-input')
// Show a new message in the chat box
function addMessage(text, isMe) {
const msgEl = document.createElement('div')
msgEl.className = 'message' + (isMe ? ' me' : '')
msgEl.textContent = text
chatBox.appendChild(msgEl)
chatBox.scrollTop = chatBox.scrollHeight
}
// When the server sends a message
ws.addEventListener('message', event => {
addMessage(event.data, false)
})
// When the form is submitted
form.addEventListener('submit', e => {
e.preventDefault()
const text = input.value.trim()
if (!text) return
// Show our own message instantly
addMessage(text, true)
// Send it to the server
ws.send(text)
// Clear the input
input.value = ''
})
Key points:
- We open a WebSocket connection to the same host (
ws://${location.host}) – that works whether you run locally or deploy later. - Incoming messages are displayed as “other” messages. Our own messages get a different style (
meclass) so they line up on the right. - The form’s default submit behavior is prevented; we send the text over the socket instead.
Step 4: Run It
Back in the terminal, start the server:
node server.js
You should see:
CodeCraft server listening on http://localhost:3000
Open a browser and go to http://localhost:3000. You’ll see the CodeCraft chat page. Open another tab or another browser on the same address, type a message in one window, and watch it appear instantly in the other. That’s real‑time!
A Few Tips from CodeCraft
- Error handling – In a production app you’d want to catch errors on the WebSocket (
ws.onerror) and maybe try to reconnect if the connection drops. - User names – Right now every message is just plain text. You can prepend a name before sending, or send a JSON object like
{user:"Alice",msg:"Hi"}and render it nicely. - Security – When you move to HTTPS you’ll need
wss://(WebSocket Secure) instead ofws://. Thewslibrary works the same; just pass the HTTPS server to it. - Scaling – A single Node process works for a few users, but for many concurrent connections you’d look at a message broker (Redis, RabbitMQ) or a cloud service that handles WebSocket scaling.
If you want to stretch your skills beyond this chat demo, try making your first open‑source contribution to a JavaScript library.
Wrap‑Up
That’s it – a complete, working real‑time chat built with just Node.js, Express, and the ws library. The whole thing is under 100 lines of code, and you can see exactly how data moves from one browser to another. On CodeCraft I love showing how a small piece of code can do something that feels “big”. Play around: add emojis, store chat history in a file, or turn it into a simple support widget for a personal site.
When you're ready to give back, learn how to contribute to a popular open‑source project and grow your developer profile.
Happy coding, and enjoy the instant chat vibes!
- →
- →
- →
- →
- →