---
slug: chat-ui-design-philosophy
tags: [design-philosophy, chat-ui, ai-ux, ui, streaming, agents]
---
# Chat UI design philosophy: show the work, keep the user in charge

A chat interface for an AI isn't a messaging app. It's a live window onto a process: the model reads, thinks, calls tools, streams an answer and cites where it came from. Around it sits a workspace of past conversations people return to. Good chat UIs give each of those moments its own honest surface, keep the answer primary, and never take control away from the person. This post distills that view from current component libraries (aicss, prompt-kit, AI Elements) and from research-backed guidelines (NN/g, Microsoft HAX, IBM, Shape of AI).

## When this applies

- **Fits:** interfaces where a person talks to a model or agent: assistants, copilots, support bots, agent consoles with tool use.
- **Partly fits:** a one-shot generator ("write me a caption") needs streaming, stop and output shape, not a history sidebar. A helper panel inside another product needs the thread principles but should use the host app's navigation instead of its own sidebar.
- **Doesn't fit:** people messaging each other. They don't need tool cards, citations or approval steps, and messaging apps have their own conventions (read receipts, presence). And don't force chat onto a task a form does better: if users always give the same three inputs, give them three fields.

## The core idea

Model the conversation as **parts, not strings**. A single assistant turn can contain reasoning, tool calls with inputs and outputs, streamed text, code, tables and sources. Libraries like AI Elements render messages from typed parts (text, reasoning, tool) straight off the AI SDK's stream, and aicss is organized the same way: thinking, tool and action states, text outputs, structured outputs, interactive blocks. Once each part has its own component, most of the principles below come naturally.

## Principles

### 1. Stream everything, and keep it stable while it streams

- Stream tokens as they arrive. A spinner says "wait"; streaming says "working".
- Partial markdown is the hard part: half-open code fences, bold markers and tables arrive unfinished. Use a renderer built for streaming (Streamdown styles unterminated blocks) or one that renders incrementally instead of re-parsing the whole message on every chunk.
- Always offer a stop control. Shape of AI calls it Controls: pause or stop mid-stream to adjust. Microsoft's HAX guidelines say to support efficient dismissal.

### 2. The scroll follows the user, not the model

- Stick to the bottom while new content streams in, but the moment the user scrolls up to read, stop following them.
- Show a "scroll to bottom" button while they're away (prompt-kit's ScrollButton), and return smoothly when they click it.
- Never let content above the viewport shove what they're reading. The use-stick-to-bottom hook exists for exactly this: it handles scroll anchoring, lets the user cancel stickiness at any time, and uses spring-based scrolling because fixed-duration easing breaks when chunks arrive at unpredictable sizes.

### 3. Show the work, collapsed by default

- Give thinking its own quiet state (a shimmer, a "Thinking…" bar, orbs) instead of an empty bubble.
- Render every tool call as a card: what was called, with what input, its status, its output or error. File diffs, image generations and to-dos get purpose-built views (aicss has examples of each).
- Put reasoning and step logs behind a disclosure. Shape of AI's "Stream of Thought" and "Footprints" patterns exist for oversight and auditability. That doesn't mean the answer should be buried under them.

### 4. Answer in the right shape

- Text is one output type among many. A comparison belongs in a table, a plan in a checklist, code in a block with a filename and a copy button.
- Structured outputs (data tables, comparison tables, to-do lists) are first-class components in aicss, not markdown afterthoughts.
- Lead with the answer; let detail expand on demand.

### 5. Ask before acting, and ask to clarify

- For consequential actions, show the plan and get approval first: an approval card or multiple-choice questions (aicss's Approval Card; Shape of AI's "Action plan" and "Verification").
- For vague prompts, ask helping questions. NN/g's study of 425 conversations found users often start underspecified and "funnel" down; the bot should help them narrow the request, not guess.

### 6. Earn trust with sources and honesty

- Cite inline, with numbered markers linking to source cards (aicss Inline Citations, prompt-kit's Source component, Shape of AI "Citations").
- Say what the system can and can't do, and how reliable it is. HAX: "make clear what the system can do" and "make clear how well the system can do what it can do". IBM's principles: design for appropriate trust and reliance, and design for imperfection.
- Make correction cheap: edit and resend, regenerate, branch. HAX: support efficient correction.
- Only cite what the model actually used. A decorative citation is worse than none.

### 7. Help people start and keep going

- Beat the blank box with suggestions, example prompts and templates (Shape of AI's "Wayfinders").
- After a complex answer, offer follow-up prompts. NN/g recommends offering both depth-focused and breadth-focused follow-ups for exploratory conversations.
- Don't judge success by conversation length. NN/g found no correlation between a conversation's length and how helpful or trustworthy users rated it.

### 8. Build on portable primitives you own

- Keep the UI provider-agnostic: the same input, message list and layout whether the stream comes from OpenAI, Mistral or anything behind the Vercel AI SDK. Portability is prompt-kit's stated goal.
- Prefer components you copy into your codebase (prompt-kit and AI Elements install through the shadcn CLI; aicss blocks are copy-paste), so the code lives in your repo and can be tuned to your design system.

### 9. The sidebar: history is a workspace, not a log

People come back to conversations, so the sidebar deserves as much design as the thread.

**Structure**
- Keep "New chat" permanently at the top, with search right under it.
- Group history by recency. Vercel's open-source chatbot uses Today, Yesterday, Last 7 days, Last 30 days and Older.
- Give every chat a real title: generate one from the first message (the Vercel chatbot makes a small model call for this) and let people rename it. A column of "New chat" entries is useless.
- Mark the active chat clearly, and give every chat its own URL so it can be bookmarked, reopened in a new tab and shared.

**Actions**
- Put rename, pin, share and delete in a per-item menu.
- Confirm before deleting, then say it happened: the Vercel chatbot uses a confirmation dialog, then a "Chat deleted" toast.
- Make sharing explicit about who can see what: Private ("Only you can access this chat") versus Public ("Anyone with the link can access this chat"), as in the Vercel chatbot's visibility selector.

**Loading and empty states**
- Page the history in (the Vercel chatbot loads 20 at a time as you scroll) and show skeleton rows while it loads. Never make the conversation wait on the history request.
- Use the empty state to teach: "Your conversations will appear here once you start chatting!" If history needs an account, say so ("Login to save and revisit previous chats!").

**Layout**
- Make it collapsible, either to an icon rail or fully off-canvas (shadcn's Sidebar supports both), with a keyboard toggle (shadcn's default is Cmd+B / Ctrl+B).
- On phones it becomes a drawer over the chat, not a column squeezing it.
- On laptops, protect the reading width of the conversation first; the sidebar is secondary.

**Beyond a flat list**
- Branches: when someone forks a conversation, keep the route back to the original (Shape of AI's "Branches") rather than scattering unrelated copies through the list.
- Temporary chats: offer a mode that neither touches memory nor clutters the sidebar. Shape of AI's "Incognito Mode" names exactly that reason: quick queries "without cluttering their sidebar".
- Memory is not history. Show what the assistant remembers in its own place, with controls to review and edit it (Shape of AI's "Memory").

## Checklist

1. Stream tokens; render partial markdown cleanly; show a stop button.
2. Stick to the bottom, release on user scroll, show "scroll to bottom", no layout jumps.
3. A distinct thinking state; tool calls as cards with status and errors; reasoning collapsed.
4. Tables, checklists and code blocks where they fit; answer first.
5. Approval before consequential actions; clarifying questions for vague asks.
6. Inline citations to real sources; clear limits; one-click edit, retry and branch.
7. Starter suggestions; depth and breadth follow-ups.
8. Provider-agnostic components that you own.
9. Sidebar: New chat and search on top; recency groups; real titles; a URL per chat; rename, share and delete (with confirmation); paged loading; collapsible with a shortcut; temporary chats.

## Applied well vs. misapplied

| Idea | Applied well | Misapplied |
|---|---|---|
| Streaming | Tokens land in place; a half-open code fence already renders as a code block | Text streams, but the layout jumps each time a markdown block closes |
| Scroll | Follows new output until the user scrolls up, then waits behind a "scroll to bottom" button | Drags the user back down while they're reading |
| Show the work | "Searched 3 sources" collapsed above the answer | Full reasoning open by default, the answer below the fold |
| Right shape | A comparison rendered as a table | The same comparison as four paragraphs |
| Ask first | An approval card listing the 12 files about to be deleted | A confirm dialog on every harmless read, so people stop reading them |
| Citations | A numbered marker linking to the source the model used | A row of source chips that don't support the sentence |
| Sidebar | Titled chats grouped by date, each with its own URL | A column of "New chat", or a history sidebar on a one-shot tool |

## Anti-patterns

- The whole answer appearing at once after a long spinner.
- Auto-scrolling while the user is reading above.
- Raw chain-of-thought expanded by default, pushing the answer off-screen.
- A wall of text where a table or checklist would do.
- Citations that don't support the claim.
- Actions taken silently without a preview or undo.
- A history sidebar full of identical "New chat" titles.
- Deleting a chat with no confirmation and no feedback.
- A share button that doesn't say who will be able to see the chat.

## References

- [aicss: UI components for AI agents](https://www.aicss.dev/): copy-paste blocks for everything an agent shows mid-conversation: thinking states, tool calls, file diffs, streaming text, inline citations, tables, approval cards.
- [prompt-kit: AI SDK UI](https://www.prompt-kit.com/ai-sdk): provider-agnostic chat components (prompt input, chat container, scroll button, reasoning, tool, source). Also publishes an llms.txt.
- [AI Elements (Vercel)](https://elements.ai-sdk.dev/): shadcn-based components for message parts, reasoning panels, tool calls and response actions on top of the AI SDK.
- [Introducing AI Elements (Vercel changelog)](https://vercel.com/changelog/introducing-ai-elements): the rationale and a useChat + Message example.
- [Streamdown](https://streamdown.ai/): a markdown renderer designed for streaming AI output, including unterminated blocks.
- [use-stick-to-bottom](https://github.com/stackblitz-labs/use-stick-to-bottom): the scroll behavior chat needs (stick, release on user scroll, scroll anchoring, spring animation).
- [The Shape of AI](https://www.shapeof.ai/): Emily Campbell's pattern library: wayfinders, inputs, tuners, governors, trust builders, identifiers.
- [The 6 Types of Conversations with Generative AI (NN/g)](https://www.nngroup.com/articles/AI-conversation-types/): diary-study research on how people actually converse with AI and what the UI should do for each type.
- [Guidelines for Human-AI Interaction (Microsoft HAX)](https://www.microsoft.com/en-us/haxtoolkit/ai-guidelines/): 18 evidence-based guidelines across initial use, interaction, when wrong, and over time.
- [Design Principles for Generative AI Applications (IBM, CHI 2024)](https://arxiv.org/abs/2401.14484): six principles including design for generative variability, appropriate trust and reliance, and imperfection.
- [People + AI Guidebook (Google PAIR)](https://pair.withgoogle.com/guidebook/): Google's guide to designing human-centered AI products.
- [assistant-ui](https://www.assistant-ui.com/): another open-source React library for AI chat interfaces, useful for comparison.
- [Vercel Chatbot (open source)](https://github.com/vercel/chatbot): a full reference app; its sidebar history shows date grouping, paged loading, delete confirmation, private/public sharing and auto-generated titles.
- [Sidebar (shadcn/ui)](https://ui.shadcn.com/docs/components/sidebar): a composable sidebar with offcanvas and icon-collapse modes, a mobile variant and a Cmd+B / Ctrl+B toggle.
- [prompt-kit blocks](https://www.prompt-kit.com/blocks): includes a "Sidebar with chat history" block grouped by Today, Yesterday and Last 7 days.
- [assistant-ui Thread list](https://www.assistant-ui.com/docs/ui/thread-list): conversation switching with search, active selection and thread actions.
- [Branches (Shape of AI)](https://www.shapeof.ai/patterns/branches): forking a conversation without losing the path back.
- [Incognito Mode (Shape of AI)](https://www.shapeof.ai/patterns/incognito-mode): sessions outside memory that don't clutter the sidebar.
- [Memory (Shape of AI)](https://www.shapeof.ai/patterns/memory): continuity across sessions, and the controls it needs.
- [Anti-vibe-coded UI (ayeeye)](https://ayeeye.net/posts/claude-opus/anti-vibe-coded-ui): the hub's baseline design values. Chat UIs are mostly copy (buttons, errors, empty states, suggestions), and its writing rules apply to all of it.
