The simple chat box is the most important user interface paradigm of the 21st century. Designing a genuinely great AI chat experience requires meticulously crafting an interaction that feels organically alive.
1The Anatomy of a Chat Bubble
A chat bubble isn't just a box with text; it's a dynamic renderer. In a professional AI application, the bubble must rigorously handle Markdown to support nested lists, bolding, and hyperlinking. Crucially, it must parse and display technical content perfectly using tools like react-markdown alongside syntax highlighters.
Furthermore, you must build in essential Quality of Life features like Copy Buttons for code blocks and Feedback Icons to passively collect human training data.
import ReactMarkdown from 'react-markdown';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
const ChatBubble = ({ text }) => (
{String(children).replace(/\n$/, '')}
) : (
{children}
)
}
}}
>
{text}
);2Managing Perceived Latency
AI models do not return answers instantly; complex reasoning takes time. To prevent users from mistakenly assuming your application has crashed, you must aggressively manage perceived latency.
You must instantly trigger subtle Typing Indicators or elegant Skeleton Loaders the very millisecond a user submits a prompt. This crucial visual feedback psychologically bridges the gap between the human request and the start of the machine's streaming response.
if (isLoading && !messages.length) {
return (
);
}
if (isWaitingForFirstToken) {
return ;
}Status: Processing Request
3The Flow of the Thread
As an AI begins streaming its response, the height of the chat window grows rapidly. You must implement robust Auto-scroll behavior that smoothly locks the viewport to the newest incoming word.
However, if a user deliberately scrolls up to re-read earlier context, your UI must intelligently detect this and immediately detach the auto-scroll. Violently yanking the screen away from a reading user is a cardinal sin of UI design.
useEffect(() => {
if (!chatContainerRef.current) return;
const isScrolledUp =
chatContainerRef.current.scrollHeight - chatContainerRef.current.scrollTop
> chatContainerRef.current.clientHeight + 100;
if (!isScrolledUp) {
bottomMarkerRef.current?.scrollIntoView({ behavior: 'smooth' });
}
}, [messages]);Action: Auto-scroll Enabled
User Scrolling: TRUE
Action: Auto-scroll Paused
