Project Idea: Real-time Chat App
This project moves beyond simple CRUD apps. You'll build a full-stack chat application, similar to Slack or Discord, where users can join different rooms and send messages that appear instantly for everyone else in the room—all without reloading the page. It's the perfect project to understand the magic of WebSockets.
✨ Key Features
- Users can enter a username and a "Room ID".
- Multiple users can join the same room.
- Messages sent by one user are broadcasted instantly to all other users in that room.
- A "User has joined" or "User has left" notification will be displayed.
💻 Tech Stack & Skills
- Tech Stack: React (Frontend), Node.js/Express (Backend), `socket.io` (for WebSockets), `socket.io-client` (React client).
- Skills Practiced: Real-time Communication, WebSocket Event Handling (joining rooms, broadcasting messages), Backend API setup, Frontend State Management (handling live data).
🛠️ Full Step-by-Step Tutorial
Part 1: The Backend (Node.js & Socket.io Server)
Step 1.1: Setup Project
- Create a
backendfolder, runnpm init -y. - Install dependencies:
npm install express socket.io cors nodemon
Step 1.2: Create the Server (`index.js`)
This is the only file we need for this simple backend. It creates an Express server, attaches Socket.io to it, and defines all the chat logic.
[attachment_0](attachment) backend/index.jsconst express = require('express');
const http = require('http');
const { Server } = require("socket.io");
const cors = require('cors');
const app = express();
app.use(cors());
const server = http.createServer(app);
// Initialize Socket.io server
const io = new Server(server, {
cors: {
origin: "http://localhost:3000", // Your React app's URL
methods: ["GET", "POST"],
},
});
// --- Socket.io Logic ---
io.on('connection', (socket) => {
console.log(`User Connected: ${socket.id}`);
// 1. Logic to join a room
socket.on('join_room', (data) => {
socket.join(data.room);
console.log(`User ${socket.id} joined room ${data.room}`);
// Notify others in the room
socket.to(data.room).emit('user_joined', { username: data.username });
});
// 2. Logic to handle sending a message
socket.on('send_message', (data) => {
// Broadcast the received message to everyone in that room
io.to(data.room).emit('receive_message', data);
});
// 3. Logic for disconnection
socket.on('disconnect', () => {
console.log(`User Disconnected: ${socket.id}`);
// (We can add logic here to notify rooms if we store which user is in which room)
});
});
// --- End Socket.io Logic ---
server.listen(5000, () => {
console.log('Server is running on port 5000');
});
Your backend is now complete! Run it with nodemon index.js.
Part 2: The Frontend (React App)
Step 2.1: Setup Project
- Create a
frontendapp:npx create-react-app frontend. - Install libraries:
npm install socket.io-client react-router-dom
Step 2.2: Create Socket.io Helper (`socket.js`)
It's good practice to create one single socket connection. Create frontend/src/socket.js.
import { io } from 'socket.io-client';
// Connect to your backend server
const socket = io('http://localhost:5000');
export default socket;
Step 2.3: Setup Routing (`App.js`)
We need two pages: a "Join" page and the "Chat" page.
frontend/src/App.jsimport React, { useState } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import JoinPage from './components/JoinPage';
import ChatRoom from './components/ChatRoom';
import './App.css'; // We'll create this
function App() {
const [username, setUsername] = useState('');
const [room, setRoom] = useState('');
return (
}
/>
}
/>
);
}
export default App;
Step 2.4: Create `JoinPage.js` Component
Create frontend/src/components/JoinPage.js.
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import socket from '../socket'; // Import our socket connection
function JoinPage({ setUsername, setRoom }) {
const [nameInput, setNameInput] = useState('');
const [roomInput, setRoomInput] = useState('');
const navigate = useNavigate();
const joinChat = () => {
if (nameInput.trim() && roomInput.trim()) {
// Set username and room in App.js state
setUsername(nameInput);
setRoom(roomInput);
// Emit "join_room" event to server
socket.emit('join_room', { username: nameInput, room: roomInput });
// Navigate to the chat room
navigate('/chat');
}
};
return (
Join a Chat Room
setNameInput(e.target.value)}
/>
setRoomInput(e.target.value)}
/>
);
}
export default JoinPage;
Step 2.5: Create `ChatRoom.js` Component
Create frontend/src/components/ChatRoom.js. This is where the main chat logic lives.
import React, { useState, useEffect, useRef } from 'react';
import socket from '../socket'; // Import our socket connection
function ChatRoom({ username, room }) {
const [currentMessage, setCurrentMessage] = useState('');
const [messageList, setMessageList] = useState([]);
const messagesEndRef = useRef(null); // To auto-scroll
const sendMessage = async () => {
if (currentMessage.trim()) {
const messageData = {
room: room,
author: username,
message: currentMessage,
time: new Date().toLocaleTimeString(),
};
await socket.emit('send_message', messageData);
// We'll receive this message back via 'receive_message'
setCurrentMessage('');
}
};
// This useEffect listens for incoming messages
useEffect(() => {
const messageListener = (data) => {
setMessageList((list) => [...list, data]);
};
const joinListener = (data) => {
const joinMessage = {
author: 'System',
message: `${data.username} has joined the room.`,
time: new Date().toLocaleTimeString(),
};
setMessageList((list) => [...list, joinMessage]);
};
socket.on('receive_message', messageListener);
socket.on('user_joined', joinListener);
// Cleanup function to remove listener
return () => {
socket.off('receive_message', messageListener);
socket.off('user_joined', joinListener);
};
}, []);
// This useEffect handles auto-scrolling
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messageList]);
return (
Live Chat: Room '{room}'
{messageList.map((msg, index) => (
{msg.message}
{msg.author}
{msg.time}
))}
setCurrentMessage(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
/>
);
}
export default ChatRoom;
Step 2.6: Basic Styling (`App.css`)
Create frontend/src/App.css to make it look like a chat app.
.App {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #020617;
font-family: 'Inter', sans-serif;
}
/* --- Join Page --- */
.join-container {
display: flex;
flex-direction: column;
gap: 15px;
width: 350px;
}
.join-container input {
padding: 12px 15px;
font-size: 1rem;
border-radius: 8px;
border: 1px solid #334155;
background-color: #1e293b;
color: #fff;
}
.join-container .cta-button {
text-align: center;
text-decoration: none;
line-height: 1.5;
}
/* --- Chat Window --- */
.chat-window {
width: 500px;
height: 600px;
background-color: #0f172a;
border: 1px solid #1e293b;
border-radius: 12px;
display: flex;
flex-direction: column;
}
.chat-header {
background-color: #1e293b;
padding: 15px;
border-top-left-radius: 12px;
border-top-right-radius: 12px;
}
.chat-header h3 {
margin: 0;
color: #fff;
}
.chat-body {
flex-grow: 1;
padding: 15px;
overflow-y: auto;
color: #cbd5e1;
}
.message {
margin-bottom: 15px;
display: flex;
}
.message-content {
max-width: 70%;
padding: 10px 15px;
border-radius: 10px;
}
.message-meta {
font-size: 0.8rem;
color: #94a3b8;
margin-top: 3px;
}
/* Message alignment */
#you {
justify-content: flex-end;
}
#you .message-content {
background-color: #a78bfa;
color: #fff;
}
#you .message-meta {
text-align: right;
}
#other {
justify-content: flex-start;
}
#other .message-content {
background-color: #334155;
color: #e2e8f0;
}
/* System Message */
#other .message-content p:first-child:contains("System") {
font-style: italic;
color: #94a3b8;
}
.chat-footer {
display: flex;
padding: 10px;
border-top: 1px solid #1e293b;
}
.chat-footer input {
flex-grow: 1;
padding: 12px 15px;
font-size: 1rem;
border-radius: 8px;
border: 1px solid #334155;
background-color: #1e293b;
color: #fff;
margin-right: 10px;
}
.chat-footer button {
padding: 10px 15px;
font-size: 1.2rem;
border: none;
border-radius: 8px;
background: #a78bfa;
color: #fff;
cursor: pointer;
}
Done! Ab aapke dono backend aur frontend servers ko chalaayein. Aap `localhost:3000` par jaakar ek room join kar sakte hain (aur kisi doosre browser tab mein bhi same room join karke) real-time chat kar sakte hain!
🔥 Next Steps & Challenges
After completing this tutorial, try these ideas:
- **Typing Indicators:** Emit a "typing" event to the server to show when another user is typing.
- **User List:** Show a list of all active users currently in the room.
- **Disconnect Notification:** Logic to inform the room when a user (e.g., "MSMAXPRO") has disconnected.
- **Database Integration:** Save all messages to a MongoDB database so the chat history is persistent.