Building a Real-Time Chat Component
Display live messages in a scrolling chat list using WebSocket events and React state.
Building a Real-Time Chat Component is a free React Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the React Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Chat Component Architecture
A real-time chat has three parts: a message list (display), a message input (send), and the WebSocket/Socket.IO connection layer. Keep the socket logic in a custom hook.
Message Data Shape
Define a TypeScript interface for chat messages to keep the component type-safe throughout.
interface Message {
id: string;
text: string;
sender: string;
timestamp: number;
}
const [messages, setMessages] = useState<Message[]>([]);useChat Hook
Encapsulate connection, message state, and send function in a single hook.
function useChat(roomId: string) {
const [messages, setMessages] = useState<Message[]>([]);
useEffect(() => {
socket.connect();
socket.emit('join-room', roomId);
socket.on('message', (msg: Message) => setMessages(m => [...m, msg]));
return () => {
socket.off('message');
socket.emit('leave-room', roomId);
socket.disconnect();
};
}, [roomId]);
const sendMessage = useCallback((text: string) => {
const msg: Message = { id: crypto.randomUUID(), text, sender: 'me', timestamp: Date.now() };
socket.emit('message', msg);
}, []);
return { messages, sendMessage };
}Message List Component
Render messages in a scrolling list. Use useRef on the bottom sentinel and scroll to it when new messages arrive.
function MessageList({ messages }: { messages: Message[] }) {
const bottomRef = useRef<HTMLDivElement>(null);
useEffect(() => {
bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
return (
<div className="message-list">
{messages.map(msg => (
<div key={msg.id} className={`msg ${msg.sender === 'me' ? 'mine' : 'theirs'}`}>
<span>{msg.sender}</span>
<p>{msg.text}</p>
</div>
))}
<div ref={bottomRef} />
</div>
);
}Message Input Component
A controlled input that emits on Enter or button click. Clear the input after sending.
function MessageInput({ onSend }: { onSend: (text: string) => void }) {
const [text, setText] = useState('');
const handleSend = () => {
if (!text.trim()) return;
onSend(text.trim());
setText('');
};
return (
<div className="input-row">
<input
value={text}
onChange={e => setText(e.target.value)}
onKeyDown={e => e.key === 'Enter' && handleSend()}
placeholder="Type a message..."
/>
<button onClick={handleSend}>Send</button>
</div>
);
}Assembling the Chat UI
Compose the hook and sub-components into a ChatRoom component.
function ChatRoom({ roomId }: { roomId: string }) {
const { messages, sendMessage } = useChat(roomId);
return (
<div className="chat-room">
<h2>Room: {roomId}</h2>
<MessageList messages={messages} />
<MessageInput onSend={sendMessage} />
</div>
);
}Optimistic Updates
Add the message to local state immediately before the server confirms it, giving instant feedback. Reconcile or remove if the server returns an error.
const sendMessage = useCallback((text: string) => {
const optimistic: Message = { id: crypto.randomUUID(), text, sender: 'me', timestamp: Date.now() };
setMessages(m => [...m, optimistic]); // instant feedback
socket.emit('message', optimistic, (err) => {
if (err) setMessages(m => m.filter(msg => msg.id !== optimistic.id));
});
}, []);Typing Indicators
Emit a typing event on input change (debounced) and listen for others' typing events to display an indicator.
const emitTyping = useMemo(() => debounce(() => socket.emit('typing'), 300), []);
useEffect(() => {
socket.on('user-typing', (user) => setTypingUser(user));
return () => socket.off('user-typing');
}, []);Message Timestamps
Format timestamps with Intl.DateTimeFormat for locale-aware display.
function formatTime(ts: number) {
return new Intl.DateTimeFormat('en', { hour: '2-digit', minute: '2-digit' }).format(ts);
}
<span className="timestamp">{formatTime(msg.timestamp)}</span>Read Receipts
Emit a read event when the user views messages and listen for read confirmations from the server to show check marks.
Virtualized Message List
For long chat histories, use react-window or react-virtual to virtualize the message list and render only visible rows.
Limiting Message History
Cap the messages array in state to prevent unbounded memory growth in long sessions.
socket.on('message', (msg: Message) => {
setMessages(prev => [...prev.slice(-499), msg]); // keep last 500
});Quick Check
What is the purpose of scrolling to the bottom sentinel div when new messages arrive in a chat list?
Recap
Build a real-time chat with a useChat hook encapsulating socket logic, a MessageList with auto-scroll, and a MessageInput that clears on send. Use optimistic updates for instant feedback and cap message history to avoid memory leaks.
Frequently asked questions
Is the “Building a Real-Time Chat Component” lesson free?
Yes — the full text of “Building a Real-Time Chat Component” is free to read here on the web, and the React Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the React Academy course, upgrade to CoddyKit PRO.
What will I learn in “Building a Real-Time Chat Component”?
Display live messages in a scrolling chat list using WebSocket events and React state. You practise React Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start React Academy?
No prior experience is required. React Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Building a Real-Time Chat Component” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this React Academy lesson?
Yes. Every React Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- WebSocket Fundamentals in the Browser
- Using Socket.IO with React
- Building a Real-Time Chat Component
- Reconnection, Error Handling & Cleanup