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. This is 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, and your progress syncs across the web and the CoddyKit app. The React Academy course includes 4 lessons in total.

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. 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. The React Academy course includes 4 lessons in total.

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, so you can start here or from the beginning and move at your own pace. This is lesson 3 of 4.

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