Use Case
/
Rapid Development
Quickly generate functional prototypes
Model
•
Claude 3.7 Sonnet
Prototype a real-time chat app with React, Node.js, and Socket.io—basic rooms, messaging, and user presence
Real-Time Chat App Prototype with React, Node.js, and Socket.io
I'll guide you through creating a basic real-time chat application with room support, messaging, and user presence indicators. Let's break this down into frontend and backend components.
Project Structure
1chat-app/
2├── client/ # React frontend
3│ ├── public/
4│ └── src/
5├── server/ # Node.js backend
6└── package.json
Backend (Node.js + Socket.io)
1. Setup the server
First, create the server directory and initialize:
1mkdir -p chat-app/server
2cd chat-app/server
3npm init -y
4npm install express socket.io cors
2. Create server.js
1const express = require('express');
2const http = require('http');
3const socketIo = require('socket.io');
4const cors = require('cors');
5
6const app = express();
7app.use(cors());
8
9const server = http.createServer(app);
10const io = socketIo(server, {
11 cors: {
12 origin: "http://localhost:3000",
13 methods: ["GET", "POST"]
14 }
15});
16
17// Track users in each room
18const rooms = {};
19
20io.on('connection', (socket) => {
21 console.log('New client connected:', socket.id);
22
23 // User joins a room
24 socket.on('join_room', ({ username, room }) => {
25 socket.join(room);
26
27 // Initialize room if it doesn't exist
28 if (!rooms[room]) {
29 rooms[room] = [];
30 }
31
32 // Add user to room
33 rooms[room].push({ id: socket.id, username });
34
35 // Broadcast to room that user has joined
36 io.to(room).emit('user_joined', {
37 username,
38 users: rooms[room],
39 message: `${username} has joined the room.`
40 });
41
42 // Send room history to the new user
43 socket.emit('room_users', rooms[room]);
44
45 console.log(`${username} joined room: ${room}`);
46 });
47
48 // Handle messages
49 socket.on('send_message', ({ room, username, message }) => {
50 const timestamp = new Date().toISOString();
51 io.to(room).emit('receive_message', {
52 username,
53 message,
54 timestamp
55 });
56 });
57
58 // Handle user typing
59 socket.on('typing', ({ room, username }) => {
60 socket.to(room).emit('user_typing', { username });
61 });
62
63 // Handle user disconnect
64 socket.on('disconnect', () => {
65 console.log('Client disconnected:', socket.id);
66
67 // Find which room the user was in
68 for (const room in rooms) {
69 const userIndex = rooms[room].findIndex(user => user.id === socket.id);
70
71 if (userIndex !== -1) {
72 const username = rooms[room][userIndex].username;
73
74 // Remove user from room
75 rooms[room].splice(userIndex, 1);
76
77 // Notify room that user has left
78 io.to(room).emit('user_left', {
79 username,
80 users: rooms[room],
81 message: `${username} has left the room.`
82 });
83
84 break;
85 }
86 }
87 });
88});
89
90const PORT = process.env.PORT || 4000;
91server.listen(PORT, () => {
92 console.log(`Server running on port ${PORT}`);
93});
Frontend (React + Socket.io-client)
1. Setup the React app
1npx create-react-app chat-app/client
2cd chat-app/client
3npm install socket.io-client
2. Create Components
src/App.js
1import React, { useState } from 'react';
2import './App.css';
3import JoinRoom from './components/JoinRoom';
4import ChatRoom from './components/ChatRoom';
5
6function App() {
7 const [joined, setJoined] = useState(false);
8 const [userData, setUserData] = useState({ username: '', room: '' });
9
10 const handleJoinRoom = (username, room) => {
11 setUserData({ username, room });
12 setJoined(true);
13 };
14
15 return (
16 <div className="App">
17 <header className="App-header">
18 <h1>Real-Time Chat App</h1>
19 </header>
20 <main>
21 {!joined ? (
22 <JoinRoom onJoin={handleJoinRoom} />
23 ) : (
24 <ChatRoom username={userData.username} room={userData.room} />
25 )}
26 </main>
27 </div>
28 );
29}
30
31export default App;
src/components/JoinRoom.js
1import React, { useState } from 'react';
2
3function JoinRoom({ onJoin }) {
4 const [username, setUsername] = useState('');
5 const [room, setRoom] = useState('');
6
7 const handleSubmit = (e) => {
8 e.preventDefault();
9 if (username && room) {
10 onJoin(username, room);
11 }
12 };
13
14 return (
15 <div className="join-container">
16 <h2>Join a Chat Room</h2>
17 <form onSubmit={handleSubmit}>
18 <div className="form-group">
19 <label htmlFor="username">Username:</label>
20 <input
21 type="text"
22 id="username"
23 value={username}
24 onChange={(e) => setUsername(e.target.value)}
25 required
26 />
27 </div>
28 <div className="form-group">
29 <label htmlFor="room">Room:</label>
30 <input
31 type="text"
32 id="room"
33 value={room}
34 onChange={(e) => setRoom(e.target.value)}
35 required
36 />
37 </div>
38 <button type="submit">Join Room</button>
39 </form>
40 </div>
41 );
42}
43
44export default JoinRoom;
src/components/ChatRoom.js
1import React, { useState, useEffect, useRef } from 'react';
2import io from 'socket.io-client';
3
4const ENDPOINT = 'http://localhost:4000';
5
6function ChatRoom({ username, room }) {
7 const [messages, setMessages] = useState([]);
8 const [message, setMessage] = useState('');
9 const [users, setUsers] = useState([]);
10 const [typingUsers, setTypingUsers] = useState([]);
11 const socketRef = useRef();
12 const messagesEndRef = useRef();
13
14 // Initialize socket connection
15 useEffect(() => {
16 socketRef.current = io(ENDPOINT);
17
18 // Join room
19 socketRef.current.emit('join_room', { username, room });
20
21 // Listen for messages
22 socketRef.current.on('receive_message', (data) => {
23 setMessages((prevMessages) => [...prevMessages, data]);
24 });
25
26 // Listen for user joined
27 socketRef.current.on('user_joined', (data) => {
28 setUsers(data.users);
29 setMessages((prevMessages) => [
30 ...prevMessages,
31 { system: true, message: data.message, timestamp: new Date().toISOString() }
32 ]);
33 });
34
35 // Listen for user left
36 socketRef.current.on('user_left', (data) => {
37 setUsers(data.users);
38 setMessages((prevMessages) => [
39 ...prevMessages,
40 { system: true, message: data.message, timestamp: new Date().toISOString() }
41 ]);
42 });
43
44 // Get current users in room
45 socketRef.current.on('room_users', (data) => {
46 setUsers(data);
47 });
48
49 // Listen for typing events
50 socketRef.current.on('user_typing', (data) => {
51 setTypingUsers((prevTypers) => {
52 if (!prevTypers.includes(data.username)) {
53 return [...prevTypers, data.username];
54 }
55 return prevTypers;
56 });
57
58 // Remove typing indicator after 2 seconds
59 setTimeout(() => {
60 setTypingUsers((prevTypers) =>
61 prevTypers.filter(user => user !== data.username)
62 );
63 }, 2000);
64 });
65
66 // Cleanup on unmount
67 return () => {
68 socketRef.current.disconnect();
69 };
70 }, [username, room]);
71
72 // Auto-scroll to bottom when new messages arrive
73 useEffect(() => {
74 messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
75 }, [messages]);
76
77 // Handle sending messages
78 const handleSendMessage = (e) => {
79 e.preventDefault();
80 if (message.trim()) {
81 socketRef.current.emit('send_message', {
82 room,
83 username,
84 message
85 });
86 setMessage('');
87 }
88 };
89
90 // Handle typing indicator
91 const handleTyping = () => {
92 socketRef.current.emit('typing', { room, username });
93 };
94
95 // Format timestamp
96 const formatTime = (timestamp) => {
97 return new Date(timestamp).toLocaleTimeString([], {
98 hour: '2-digit',
99 minute: '2-digit'
100 });
101 };
102
103 return (
104 <div className="chat-container">
105 <div className="chat-header">
106 <h2>Room: {room}</h2>
107 </div>
108
109 <div className="chat-main">
110 <div className="chat-sidebar">
111 <h3>Users in Room</h3>
112 <ul className="users-list">
113 {users.map((user, index) => (
114 <li key={index} className={user.username === username ? 'current-user' : ''}>
115 {user.username} {user.username === username ? '(You)' : ''}
116 </li>
117 ))}
118 </ul>
119 </div>
120
121 <div className="chat-messages">
122 {messages.map((msg, index) => (
123 <div
124 key={index}
125 className={`message ${msg.system ? 'system-message' :
126 (msg.username === username ? 'my-message' : 'other-message')}`}
127 >
128 {!msg.system && (
129 <div className="message-header">
130 <span className="username">{msg.username}</span>
131 <span className="timestamp">{formatTime(msg.timestamp)}</span>
132 </div>
133 )}
134 <p className="message-text">{msg.message}</p>
135 </div>
136 ))}
137
138 {typingUsers.length > 0 && (
139 <div className="typing-indicator">
140 {typingUsers.join(', ')} {typingUsers.length === 1 ? 'is' : 'are'} typing...
141 </div>
142 )}
143
144 <div ref={messagesEndRef} />
145 </div>
146 </div>
147
148 <form className="message-form" onSubmit={handleSendMessage}>
149 <input
150 type="text"
151 value={message}
152 onChange={(e) => setMessage(e.target.value)}
153 onKeyPress={handleTyping}
154 placeholder="Type a message..."
155 />
156 <button type="submit">Send</button>
157 </form>
158 </div>
159 );
160}
161
162export default ChatRoom;
3. Add some basic CSS
src/App.css
1* {
2 box-sizing: border-box;
3 margin: 0;
4 padding: 0;
5}
6
7body {
8 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
9 line-height: 1.6;
10 background-color: #f5f5f5;
11}
12
13.App {
14 max-width: 1200px;
15 margin: 0 auto;
16 padding: 1rem;
17}
18
19.App-header {
20 text-align: center;
21 margin-bottom: 2rem;
22 color: #333;
23}
24
25/* Join Room Styles */
26.join-container {
27 max-width: 500px;
28 margin: 0 auto;
29 background: white;
30 border-radius: 8px;
31 padding: 2rem;
32 box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
33}
34
35.join-container h2 {
36 margin-bottom: 1.5rem;
37 text-align: center;
38}
39
40.form-group {
41 margin-bottom: 1rem;
42}
43
44.form-group label {
45 display: block;
46 margin-bottom: 0.5rem;
47 font-weight: 500;
48}
49
50.form-group input {
51 width: 100%;
52 padding: 0.75rem;
53 border: 1px solid #ddd;
54 border-radius: 4px;
55 font-size: 1rem;
56}
57
58button {
59 background-color: #4caf50;
60 color: white;
61 border: none;
62 padding: 0.75rem 1.5rem;
63 font-size: 1rem;
64 border-radius: 4px;
65 cursor: pointer;
66 transition: background-color 0.2s;
67}
68
69button:hover {
70 background-color: #45a049;
71}
72
73/* Chat Room Styles */
74.chat-container {
75 display: flex;
76 flex-direction: column;
77 height: 80vh;
78 background: white;
79 border-radius: 8px;
80 box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
81}
82
83.chat-header {
84 padding: 1rem;
85 background: #4caf50;
86 color: white;
87 border-top-left-radius: 8px;
88 border-top-right-radius: 8px;
89}
90
91.chat-main {
92 display: flex;
93 flex: 1;
94 overflow: hidden;
95}
96
97.chat-sidebar {
98 width: 200px;
99 background: #f8f8f8;
100 padding: 1rem;
101 border-right: 1px solid #ddd;
102 overflow-y: auto;
103}
104
105.chat-sidebar h3 {
106 margin-bottom: 1rem;
107 font-size: 1.1rem;
108}
109
110.users-list {
111 list-style: none;
112}
113
114.users-list li {
115 padding: 0.5rem 0;
116 border-bottom: 1px solid #eee;
117}
118
119.current-user {
120 font-weight: bold;
121 color: #4caf50;
122}
123
124.chat-messages {
125 flex: 1;
126 padding: 1rem;
127 overflow-y: auto;
128}
129
130.message {
131 margin-bottom: 1rem;
132 padding: 0.75rem;
133 border-radius: 8px;
134 max-width: 80%;
135}
136
137.my-message {
138 background-color: #e3f2fd;
139 margin-left: auto;
140}
141
142.other-message {
143 background-color: #f1f1f1;
144}
145
146.system-message {
147 background-color: #fffde7;
148 text-align: center;
149 font-style: italic;
150 padding: 0.5rem;
151 margin: 0.5rem auto;
152 max-width: 90%;
153 color: #666;
154}
155
156.message-header {
157 display: flex;
158 justify-content: space-between;
159 margin-bottom: 0.5rem;
160 font-size: 0.85rem;
161}
162
163.username {
164 font-weight: bold;
165}
166
167.timestamp {
168 color: #777;
169}
170
171.typing-indicator {
172 font-style: italic;
173 color: #777;
174 padding: 0.5rem;
175}
176
177.message-form {
178 display: flex;
179 padding: 1rem;
180 border-top: 1px solid #ddd;
181}
182
183.message-form input {
184 flex: 1;
185 padding: 0.75rem;
186 border: 1px solid #ddd;
187 border-radius: 4px;
188 margin-right: 0.5rem;
189 font-size: 1rem;
190}
Running the Application
1. Start the server:
1cd chat-app/server
2node server.js
2. Start the React client:
1cd chat-app/client
2npm start
3. Open your browser to http://localhost:3000
Features Implemented
- Room-based chat:
- Users can join specific chat rooms
- Multiple rooms can exist simultaneously
- Real-time messaging:
- Messages are delivered instantly to all users in the room
- User presence:
- Shows who is currently in the room
- Notifications when users join or leave
- Typing indicators:
- Shows when someone is typing a message
- Message formatting:
- Different styles for your messages vs. others
- System messages for join/leave events
- Timestamps on messages
This prototype provides a solid foundation for a real-time chat application. You can extend it with features like:
- User authentication
- Persistent message history
- Private messaging
- File sharing
- Read receipts
- Custom emojis/reactions
Explore Related Use Cases
Experience Ninja's AI Assistant
Try for free today. Plans starting at $5/month.