How I Created a Real-Time Chat Without Any Framework Using WebSockets

Posted by

A step-by-step guide to building a working chat app using only Node.js and pure JavaScript, no libraries, no frameworks, no shortcuts.

A step-by-step guide to building a working chat app using only Node.js and pure JavaScript, no libraries, no frameworks, no shortcuts.

Introduction

I wanted to understand how real-time chat apps like Slack or Discord actually work behind the scenes, not with a fancy framework like Socket.IO, but with pure WebSocket logic.

So I decided to challenge myself:

Build a live, real-time chat app without using any framework.

No Express. No React. No Socket.IO.
Just Node.js, native browser APIs, and the WebSocket protocol.

And honestly? It was much simpler (and more fun) than I expected.

In this post, Iโ€™ll show you exactly how I built it, step-by-step, so you can create your own from scratch and finally understand how real-time communication works under the hood.


1. Why WebSockets?

Normally, browsers use HTTP requests, which follow a simple pattern:

Client sends a request โ†’ Server responds โ†’ Connection closes.

Thatโ€™s fine for regular apps, but useless for real-time messaging, where data needs to flow in both directions instantly.

WebSockets fix this problem.

They open a persistent connection between the client and the server, allowing both sides to send and receive data freely no repeated requests, no polling, and no delay.

Perfect for:

  • Chat apps
  • Multiplayer games
  • Live notifications
  • Collaborative tools

Letโ€™s build one.


2. Setting Up the Project

First, create a fresh folder:

mkdir no-framework-chat
cd no-framework-chat
npm init -y

Then install the ws package, the only library weโ€™ll use, to create a WebSocket server in Node.js:

npm install ws

Your structure will look like this:

no-framework-chat/
โ”‚
โ”œโ”€โ”€ server.js # Node.js WebSocket server
โ””โ”€โ”€ public/
โ””โ”€โ”€ index.html # Frontend chat interface

3. Creating the WebSocket Server

In the root folder, create a file called server.js:

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

// Serve the frontend HTML file
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);
}
});

// Create a WebSocket server on top of the same HTTP server
const wss = new WebSocketServer({ server });

// When a client connects
wss.on('connection', (socket) => {
console.log('โœ… New user connected');

// Handle messages from this client
socket.on('message', (message) => {
console.log('๐Ÿ“จ Received:', message.toString());

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

socket.on('close', () => console.log('โŒ User disconnected'));
});

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

โœ… What this does:

  • Serves the frontend HTML file.
  • Creates a WebSocket server (wss).
  • Listens for incoming connections and messages.
  • Broadcasts every new message to all connected users.

No framework. Just Nodeโ€™s built-in http and ws.


4. Building the Frontend (HTML + JS Only)

Now create the public folder and inside it, make an index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>No-Framework Chat App</title>
<style>
body {
font-family: sans-serif;
background: #f2f2f2;
display: flex;
flex-direction: column;
height: 100vh;
margin: 0;
}
#chat {
flex: 1;
overflow-y: auto;
background: #fff;
padding: 10px;
border: 1px solid #ddd;
}
form {
display: flex;
border-top: 1px solid #ddd;
background: #fff;
padding: 10px;
}
input {
flex: 1;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
background: #007bff;
color: white;
border: none;
margin-left: 8px;
padding: 10px 20px;
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="form">
<input id="message" placeholder="Type a message..." autocomplete="off" />
<button>Send</button>
</form>

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

const username = prompt("Enter your name:") || "Anonymous";

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

ws.onopen = () => {
addMessage("๐ŸŸข Connected to the chat server");
};

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

form.addEventListener('submit', (e) => {
e.preventDefault();
if (input.value.trim()) {
ws.send(`${username}: ${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>

โœ… What this does:

  • Prompts for a username.
  • Connects to the WebSocket server.
  • Displays all messages instantly when sent or received.
  • Works across multiple browser tabs.

5. Running the Chat

Start the server:

node server.js

Then open two browser tabs:
๐Ÿ‘‰ http://localhost:3000

Type a message in one and youโ€™ll see it appear in both tabs instantly.

๐ŸŽ‰ You just built a real-time chat app without any framework!


6. Whatโ€™s Actually Happening

Letโ€™s walk through the flow:

  1. When you open the page, the browser connects to the WebSocket server.
const ws = new WebSocket('ws://localhost:3000');

2. The server keeps this connection open.
  When any user sends a message:

ws.send('Hello World');

3. The server receives it and loops over all connected clients:

wss.clients.forEach(client => client.send(message));

4. Every browser receives the same message instantly.

No polling. No refreshes. No frameworks.
Just a persistent two-way channel, exactly how modern apps like WhatsApp Web or Slack work.


7. Adding Features (Optional Enhancements)

Once you understand the core flow, you can enhance it easily.

โœ… Add Timestamps

const time = new Date().toLocaleTimeString();
ws.send(`[${time}] ${username}: ${input.value}`);

โœ… Display System Messages

wss.on('connection', (socket) => {
wss.clients.forEach((client) => {
if (client.readyState === 1) client.send('A new user joined the chat ๐ŸŸข');
});
});

โœ… Notify When a User Leaves

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

โœ… Add Colors for Each User

You can randomly assign a color to each username and style their messages accordingly.


8. Why I Didnโ€™t Use Socket.IO

Socket.IO is great; it adds fallbacks, room management, and reconnection logic.
But for this project, I wanted to see what really happens under the hood.

By writing this from scratch, I learned:

  • How WebSocket connections are established
  • How broadcasting works
  • How to handle disconnects
  • How minimal the real-time core actually is

Once you understand pure WebSockets, using Socket.IO feels effortless.


9. Deployment Tips

When youโ€™re ready to deploy:

  • Use wss:// instead of ws:// for HTTPS.
  • Run behind NGINX or a Node process manager like PM2.
  • Use sticky sessions if you scale horizontally (multiple servers).
  • Sanitize user inputs to prevent script injection.

You can easily host this project on:

  • Render
  • Railway.app
  • Fly.io
  • DigitalOcean Droplets

Itโ€™s that lightweight.


10. Key Takeaways

Building real-time apps doesnโ€™t require complex frameworks.
With less than 100 lines of code, you can have a fully functional live chat that runs anywhere.

WebSockets = simplicity + power.

What I learned:

  • Real-time doesnโ€™t need polling or heavy APIs.
  • Native, WebSocket and there ws are enough for small to medium apps.
  • The core idea of Slack, Discord, or Figmaโ€™s collaboration tools is surprisingly simple: just extended WebSockets.

Conclusion

Building this chat app without any framework was the best way to understand the real mechanics of live communication.

Once youโ€™ve mastered this, you can easily:

  • Add rooms and private chats.
  • Switch to Socket.IO for more features.
  • Use it for games or IoT updates.

If you can send and receive a message through a WebSocket, youโ€™ve already learned the foundation of real-time web apps.

Pro Tip: Always learn the bare metal version first. Frameworks are easier to use once you know what they abstract away.


Call to Action

Would you try building your own real-time chat without frameworks?
Share your experience (or demo link) in the comments and bookmark this post for your next side project.

Leave a Reply

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