0Pricing
React Academy · Lesson

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

  1. WebSocket Fundamentals in the Browser
  2. Using Socket.IO with React
  3. Building a Real-Time Chat Component
  4. Reconnection, Error Handling & Cleanup
← Back to React Academy