Build a Real-Time Chat App in Pure JavaScript with WebSockets

Posted by

Learn how to create a fully functional live chat app from scratch, no frameworks, no libraries, just pure JavaScript.

Learn how to create a fully functional live chat app from scratch, no frameworks, no libraries, just pure JavaScript.

Introduction

Every developer should build a real-time chat app at least once.
 It’s the perfect project to understand WebSockets, the technology that powers real-time messaging, multiplayer games, live dashboards, and collaborative tools.

In this tutorial, we’ll build a real-time chat app using only JavaScript, no frameworks, no Socket.IO, and no external libraries.

You’ll learn:

  • How WebSockets actually work
  • How to set up a Node.js WebSocket server
  • How to connect clients with native browser APIs
  • How to broadcast messages in real time

By the end, you’ll have a working chat app you can run locally or deploy anywhere.


1. What Are WebSockets?

WebSockets are a protocol that allows two-way (full-duplex) communication between a client and a server over a single TCP connection.

Unlike HTTP, where you send a request and wait for a response, WebSockets allow both the browser and the server to send data instantly.

That makes them perfect for:

  • Chat applications
  • Live dashboards
  • Multiplayer games
  • Collaborative apps

Let’s get our hands dirty.


2. Project Setup

Step 1: Create a new project

Create a new folder for your project:

mkdir websocket-chat
cd websocket-chat
npm init -y

Step 2: Install dependencies

We’ll only need one package, the official WebSocket library for Node.js:

npm install ws

This library lets us run a WebSocket server easily.

Project Structure

websocket-chat/

├── server.js # Node.js WebSocket server
└── public/
└── index.html # Frontend chat UI + WebSocket client

3. Building the WebSocket Server

Create a file called server.js in the root folder.

Paste this code:

import { WebSocketServer } from 'ws';
import { createServer } from 'http';
import { readFileSync } from 'fs';
import { resolve } from 'path';

// Create a basic HTTP server for serving HTML
const server = createServer((req, res) => {
if (req.url === '/') {
const html = readFileSync(resolve('public', 'index.html'), 'utf-8');
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(html);
}
});

const wss = new WebSocketServer({ server });

wss.on('connection', (socket) => {
console.log('New client connected');

socket.on('message', (message) => {
console.log('Received:', message.toString());

// Broadcast to all connected clients
wss.clients.forEach((client) => {
if (client.readyState === 1) {
client.send(message.toString());
}
});
});

socket.on('close', () => console.log('Client disconnected'));
});

const PORT = 3000;
server.listen(PORT, () => console.log(`Server running at http://localhost:${PORT}`));

✅ This script does three things:

  1. Serves the frontend index.html file.
  2. Opens a WebSocket connection on the same server.
  3. Broadcasts messages to all connected users.

4. Creating the Chat Frontend

Now create the public folder and add index.html inside it:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Real-Time Chat (Pure JS)</title>
<style>
body {
font-family: sans-serif;
background: #f3f3f3;
display: flex;
flex-direction: column;
height: 100vh;
margin: 0;
}
#chat {
flex: 1;
padding: 10px;
overflow-y: auto;
background: white;
border: 1px solid #ddd;
margin: 10px;
}
#messageForm {
display: flex;
padding: 10px;
background: #fff;
border-top: 1px solid #ccc;
}
#input {
flex: 1;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
margin-left: 10px;
padding: 10px 20px;
border: none;
background: #007bff;
color: white;
border-radius: 4px;
cursor: pointer;
}
</style>
</head>
<body>
<h2 style="text-align:center;">💬 Real-Time Chat (Pure JS)</h2>
<div id="chat"></div>

<form id="messageForm">
<input id="input" placeholder="Type a message..." autocomplete="off" />
<button>Send</button>
</form>

<script>
const chat = document.getElementById('chat');
const form = document.getElementById('messageForm');
const input = document.getElementById('input');

const ws = new WebSocket(`ws://${window.location.host}`);

ws.onopen = () => {
addMessage('Connected to chat server ✅');
};

ws.onmessage = (event) => {
addMessage(event.data);
};

form.addEventListener('submit', (e) => {
e.preventDefault();
if (input.value.trim()) {
ws.send(input.value);
input.value = '';
}
});

function addMessage(message) {
const div = document.createElement('div');
div.textContent = message;
chat.appendChild(div);
chat.scrollTop = chat.scrollHeight;
}
</script>
</body>
</html>

✅ This simple UI includes:

  • A chat box that displays messages.
  • A form to send messages.
  • A WebSocket connection that listens for new messages in real time.

5. Running the Chat App

Now run your app:

node server.js

Open http://localhost:3000 in two different browser tabs.
Type a message in one and watch it appear instantly in both.

🎉 Congratulations, you just built a real-time chat app from scratch using pure JavaScript.


6. How It Works (Under the Hood)

Here’s what happens step by step:

  1. The browser opens a connection:
const ws = new WebSocket('ws://localhost:3000');

2. The WebSocket handshake upgrades the HTTP connection to a persistent TCP socket.

3. When a message is sent:

ws.send('Hello');

 The server receives it and broadcasts it to all connected clients.

4. Every connected client receives the message instantly via:

ws.onmessage = (event) => console.log(event.data);

There’s no polling, no delay, no request overhead, pure, continuous communication.


7. Add Usernames (Optional Enhancement)

Let’s make it slightly more interactive by adding a username prompt.

In your index.htmlModify the script section like this:

const username = prompt('Enter your name:') || 'Anonymous';
const ws = new WebSocket(`ws://${window.location.host}`);

form.addEventListener('submit', (e) => {
e.preventDefault();
const message = `${username}: ${input.value}`;
ws.send(message);
input.value = '';
});

Now, each user’s messages appear with their name.


8. Handling Disconnections Gracefully

You can detect when users leave the chat and broadcast a “user left” message:

wss.on('connection', (socket) => {
console.log('Client connected');
socket.send('Welcome!');

socket.on('close', () => {
wss.clients.forEach((client) => {
if (client.readyState === 1) client.send('A user has left the chat.');
});
});
});

This gives your chat app a more realistic experience.


9. Security and Deployment Tips

Before deploying, consider:

  • Use wss:// instead of ws:// for HTTPS environments.
  • Limit message size to prevent spam or memory leaks.
  • Sanitize user input to avoid HTML injection.
  • Use a reverse proxy like NGINX for scalability.

You can easily deploy this on services like Render, Railway, or Vercel (with a Node backend).


10. Bonus: Add Styling or Persistence

Want to go further? Try:

  • Saving messages to a file or database.
  • Displaying timestamps.
  • Styling messages by user color.
  • Adding “user typing…” indicators.

All of these can still run over the same WebSocket connection, no new libraries needed.


Conclusion

You’ve just built a complete real-time chat app using pure JavaScript and WebSockets.

This simple example demonstrates the power of WebSockets:

  • Persistent connections
  • Real-time message delivery
  • Instant broadcasting to all clients

No frameworks. No heavy dependencies. Just native browser APIs and Node.js.

Pro Tip: Once you’re comfortable with this, explore Socket.IO, which builds on top of WebSockets with auto-reconnect, room management, and fallback support.


Call to Action

Did you follow along and build the chat app?
Share your screenshot or improvements in the comments and bookmark this post for your next WebSocket project.

Leave a Reply

Your email address will not be published. Required fields are marked *