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

Try Query
Claude 3.7 Sonnet
/
coder

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

  1. Room-based chat:
    • Users can join specific chat rooms
    • Multiple rooms can exist simultaneously
  2. Real-time messaging:
    • Messages are delivered instantly to all users in the room
  3. User presence:
    • Shows who is currently in the room
    • Notifications when users join or leave
  4. Typing indicators:
    • Shows when someone is typing a message
  5. 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

Experience Ninja's AI Assistant

Try for free today. Plans starting at $5/month.