Stop 08 · ReactProjects · 3/6

Chat UI

Part of the Frontend Developer Roadmap

In short

A chat screen looks easy until you build one. Auto-scrolling, timestamps, sending states and long messages each hide a small React lesson.

A messaging interface — message list, input box, and optionally real-time updates — great practice for list rendering, auto-scroll and controlled inputs.

The jaadu mascot holds a giant phone full of colourful chat bubbles as a small robot replies.

Think of it like this

The front of a WhatsApp group

You're not building WhatsApp's servers, just the part people see: bubbles on the left and right, a box at the bottom, and the screen jumping to the newest message. Fake the other side with a bot that replies after a second.

Watch it happen

Send, reply, scroll

You
Chat input
MessageList
Bot

"hi" + Enter

1/6You type hi and press Enter.

The big ideas

What you need to know

Bubbles by sender

Put your messages on the right and others on the left, based on a sender field.

Auto-scroll to newest

Put a ref after the last message and scroll it into view whenever messages change.

Sending states

Show a message instantly as 'sending', then mark it 'sent', like the single and double tick.

Keyboard friendly

Enter sends, Shift+Enter adds a new line, and the box keeps focus after sending.

How it works

Build plan

  1. 1

    Static layout

    A header, a scrolling message list and an input fixed at the bottom.

  2. 2

    Send messages

    Add typed messages to a messages array in state.

  3. 3

    Fake replies

    A setTimeout bot replies a second later.

  4. 4

    Scroll and polish

    Scroll to the latest message and add times and ticks.

See it in code

Walk through the code

tsx
1import { useEffect, useRef } from "react";2 3type Msg = { id: string; text: string; mine: boolean };4 5export function MessageList({ messages }: { messages: Msg[] }) {6  const endRef = useRef<HTMLDivElement>(null);7  useEffect(() => {8    endRef.current?.scrollIntoView({ behavior: "smooth" });9  }, [messages]);10  return (11    <div className="messages">12      {messages.map((m) => <p key={m.id} className={m.mine ? "me" : "them"}>{m.text}</p>)}13      <div ref={endRef} />14    </div>15  );16}

1/4endRef will point at an empty div after the last bubble.

The empty div sits after the last bubble. Every time messages changes, the effect scrolls it into view, so the newest message is always visible.

Save yourself the pain

Common mistakes

Oops

Running the scroll effect with no dependency array.

Do this instead

Pass [messages] so it scrolls only when messages change, not on every render caused by typing.

Oops

Letting the whole page scroll instead of the message list.

Do this instead

Give the list a fixed height with overflow-y: auto, and keep the header and input outside it.

Your mission

First milestone: talk to a bot

~40 min

Quick check

Why does the auto-scroll effect list [messages] as its dependency?

Go deeper

  1. 1Auto-scroll to the latest message when a new one arrives.
  2. 2Build the message list with proper `key` usage for performance.
  3. 3Add optimistic UI — show the sent message immediately, before server confirmation.

More projects at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge