Building Message Bubbles in React
A message bubble in React is a styled div inside a scrollable list, rendered from a { role, content, timestamp } data shape. This lesson builds one from scratch with auto-scroll, avatars, and status states — the foundation under every chat UI.
Data Model
Start with a clean message type:
interface Message {
id: string;
role: 'user' | 'assistant';
content: string;
timestamp: Date;
status?: 'sending' | 'sent' | 'error';
}Keep the model simple. You can extend it later with attachments, metadata, or citations, but the core is: who said what, and when.
Message Component
Each message is a flex container with conditional alignment:
- max-w-[75%] prevents full-width blobs that are hard to read
- rounded-2xl gives the modern bubble shape
- whitespace-pre-wrap preserves AI formatting and line breaks
Auto-Scrolling
Chat interfaces must auto-scroll to the latest message - but only when the user is already at the bottom. If they've scrolled up to read history, don't yank them down.
const messagesEndRef = useRef<HTMLDivElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
const isNearBottom = () => {
const el = containerRef.current;
if (!el) return true;
return el.scrollHeight - el.scrollTop - el.clientHeight < 100;
};
useEffect(() => {
if (isNearBottom()) {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}
}, [messages]);The < 100 threshold gives a small buffer - users scrolled up slightly still get auto-scroll.
Test auto-scroll on mobile especially. Touch scrolling has momentum - users may still be "scrolling" when a new message arrives. Use `behavior: 'smooth'` to avoid jarring jumps.