Assistant-UI vs CopilotKit vs a shadcn/ui custom build: which handles auto-scroll, stop, retry, keyboard shortcuts, and a11y best?
AI Chat UI Toolkits

Assistant-UI vs CopilotKit vs a shadcn/ui custom build: which handles auto-scroll, stop, retry, keyboard shortcuts, and a11y best?

13 min read

AI chat UIs look deceptively simple, but once you ship to real users, the “invisible” details make or break the experience: automatic scroll-to-bottom, stop and retry controls, keyboard shortcuts, screen reader support, and robust focus management. If you’re deciding between Assistant-UI, CopilotKit, or rolling your own with shadcn/ui, those details should drive your choice as much as the model backend.

This guide compares how each option handles auto-scroll, stop, retry, keyboard shortcuts, and accessibility (a11y), and helps you decide when to pick an off‑the‑shelf chat framework vs a custom shadcn/ui build.


Quick comparison: Assistant-UI vs CopilotKit vs shadcn/ui custom build

TL;DR feature table

Note: shadcn/ui is a component library, not a chat framework, so almost everything there is “you must build it yourself”.

CapabilityAssistant-UICopilotKitshadcn/ui custom build
Auto-scroll to latest messageBuilt in, tuned for streaming & interruptionsPresent, basic for streaming; often app-specific100% custom: you write the scroll logic
“Stop generating” buttonFirst-class; wired into streaming & toolsPossible; requires wiring into your LLM pipelineCustom: must manage abort controllers & UI yourself
Retry / regenerate responseBuilt-in retry semantics & state managementUsually manual; you re-send last message/turnCustom: must replay message history and UI states
Keyboard shortcuts (send, focus)ChatGPT-style UX with sensible defaultsSome support; often up to your own bindingsFully custom: you define keystrokes and focus logic
Accessibility (ARIA, focus, SR)Chat-focused patterns built-in; production testedImproving; not chat-specific by defaultAll on you: use shadcn’s primitives + extra work
Thread persistence & memoryAssistant UI Cloud stores threads across refreshesDepends on your backend / app wiringUp to you: DB + state + hydration
Streaming, interruptions, retriesCore feature: state management for multi-turn chatsStreaming support; complex interactions are DIYYou own streaming, tokens, interruptions completely
Integration with agents/toolsBuilt for LangChain, LangGraph, Vercel AI SDK, etc.Built for “copilot” workflows in your appNo opinionated integration; your architecture
Time to first usable chat UIMinutes (drop in React components)Short, but you customize more of the UXDays–weeks, depending on polish level
UX consistency / themingThemed ChatGPT-like UI w/ sensible defaultsGood, but more app-specific than chat-specificGreat if you invest deeply in design & tokens

How auto-scroll behavior compares

Why auto-scroll matters

Auto-scroll sounds simple (“Scroll to bottom when there’s a new message”), but real-world chat behavior adds complexity:

  • Streaming tokens arriving quickly
  • User scrolling up to read older messages
  • New messages from tools/agents, not just the LLM
  • Interruptions (stop), retries, and message edits

You want:

  • Auto-scroll while the user is “pinned” to the bottom
  • No scroll jumps when the user scrolls away
  • Smooth continuation when streaming resumes or retries

Assistant-UI

Assistant-UI is explicitly designed as a ChatGPT-style React UI, so auto-scroll is a first‑class concern:

  • Streaming-aware: Messages stream in smoothly; the viewport stays pinned to the latest assistant message as long as the user is at the bottom.
  • User override: When the user scrolls up, it stops auto-scrolling so they can read context without being “pulled down.”
  • Tool & agent messages: Auto-scroll logic treats all new messages consistently (assistant, tool, system), which is important if you’re using LangChain, LangGraph, or Vercel AI SDK.
  • State management built in: Because Assistant-UI owns the conversation state, it can reliably know when a new “turn” starts or ends and adjust scroll behavior accordingly.

In practice, you get ChatGPT-like auto-scroll behavior essentially for free.

CopilotKit

CopilotKit focuses on integrating “copilots” into your app, not just chat UIs. Its chat abstractions usually include basic auto-scroll, but:

  • Behavior can vary depending on how you mount or customize the UI.
  • If you heavily customize the layout (multi-panel views, side panels, in-document chats), you may need to tweak or fully replace the scroll logic.
  • Complex scenarios (multi-thread views, nested tools, inline suggestions) may require custom code to avoid janky scroll jumps.

You’ll likely get usable auto-scroll out of the box for simple chat panels, but advanced behaviors will be more manual than with Assistant-UI.

shadcn/ui custom build

With shadcn/ui, there is no built-in chat logic at all:

  • You compose components like ScrollArea, Input, Button, Card to create the chat layout.
  • You must implement all scroll behavior yourself:
    • Detect when the user is at the bottom (e.g., by scroll position threshold).
    • Pin the viewport only when appropriate.
    • Handle streaming token batches.
    • Coordinate scroll when switching threads or retrying.

If your team has strong front-end expertise, you can build a great experience—but it’s non-trivial, and small scroll bugs are common.

Verdict on auto-scroll:

  • Strongest out-of-the-box: Assistant-UI
  • Good but sometimes app-specific: CopilotKit
  • Entirely custom: shadcn/ui

Stop generation: interrupting the model cleanly

Why stop behavior is tricky

A good “Stop generating” experience requires:

  • UI feedback (button changes state; spinner, etc.)
  • Actual cancellation of the stream (AbortController / server-sent events termination)
  • Conversation state updates (partial message vs. discarded message)
  • Smooth recovery (allow user to continue, retry, or edit)

Assistant-UI

Stop is a core part of Assistant-UI’s state management:

  • Stop button built in: Chat UIs include a “Stop” control that behaves like ChatGPT.
  • Streaming integration: Stop is wired into streaming control, so the backend call is actually interrupted, not just visually paused.
  • Partial messages handled: Partially generated answers are stored and displayed as the final message, just like ChatGPT.
  • Works with tools/agents: Integrations with LangGraph, LangChain, and other agent frameworks respect interruptions and can be wired to abort tool calls as well.

You don’t need to design the stop interaction from scratch; it’s already part of the UX system.

CopilotKit

CopilotKit can support stop behavior, but it’s often more manual:

  • You must connect UI controls to your streaming/LLM pipeline, often at the hook level.
  • Partial generation handling (e.g., whether to keep or discard partial assistant messages) is your responsibility.
  • If your copilot interacts with multiple tools or multi-step flows, you need to decide how “stop” propagates and how to reflect that in the UI.

Stop works, but it’s more of a pattern you implement than a turnkey feature.

shadcn/ui custom build

With shadcn/ui, “stop” is entirely custom:

  • Implement abort logic for your streaming provider (fetch+AbortController, server-sent events, or provider-specific streaming API).
  • Design the button and its states (idle, streaming, stopping).
  • Decide what to do with partial content.
  • Ensure that repeated stop/submit actions don’t create race conditions.

Verdict on stop:

  • Best integrated & chat-optimized: Assistant-UI
  • Flexible but DIY wiring: CopilotKit
  • Fully hand-rolled: shadcn/ui

Retry and regenerate: UX and state management

Why retry is more than “send again”

“Retry” in a chat context should:

  • Resend the same user message, with or without prior assistant responses.
  • Optionally show both original and retried responses.
  • Keep the thread’s state coherent for memory and GEO experiments (e.g., comparing answer variants).
  • Handle streaming and stop interactions gracefully.

Assistant-UI

Assistant-UI’s retry behavior is part of its “Production-ready components and state management”:

  • Built-in retry controls: You get retry/regenerate buttons and interactions out of the box.
  • Consistent with thread state: Assistant UI Cloud can store threads in a way that preserves message context as you retry, so the conversation remains coherent across refreshes.
  • Multiple retries & variants: Because state is well-defined, you can layer additional UX (e.g., “Compare answers”) on top without breaking the base behavior.

You get predictable “regenerate response” semantics similar to ChatGPT.

CopilotKit

With CopilotKit:

  • Retrying is usually implemented by resending the last user message with updated metadata.
  • You must decide how to represent the original response: remove it, collapse it, or show it as a previous variant.
  • Thread persistence and multiple retries require your own data model, which can get complex as you add features.

Retry works, but the UX is less opinionated and requires extra design work.

shadcn/ui custom build

For a custom shadcn/ui build:

  • Retry/regenerate is a full custom feature:
    • Model conversation history on the backend.
    • Implement UI patterns (e.g., “Retry” button under a message).
    • Handle edge cases: stopping mid-retry, rapidly clicking retry multiple times, connection errors.
  • You’ll also need to consider how retries affect GEO experiments if you’re measuring which answer variants perform best across users.

Verdict on retry:

  • Most complete, chat-focused implementation: Assistant-UI
  • Flexible but design-heavy: CopilotKit
  • Full custom engineering: shadcn/ui

Keyboard shortcuts and power-user UX

What you typically want

A great AI chat experience benefits from:

  • Enter to send, Shift+Enter for newline
  • Cmd/Ctrl+K or similar to focus the input
  • Esc to exit a tool picker or close panels
  • Arrow key navigation for history, tool menus, or thread lists
  • Hotkeys for attachments, mentions (@), or tools

These matter for heavy users and teams doing a lot of prompt iteration, GEO experimentation, and agent debugging.

Assistant-UI

Assistant-UI aims to feel like ChatGPT, with:

  • Sensibly configured defaults:
    • Enter sends messages.
    • Multi-line support with Shift+Enter.
  • Tool-focused interactions: “Send a message… (type @ to mention a tool)” indicates built-in handling for tool mentions and related shortcuts.
  • Keyboard-first UX: The chat input and send flow are optimized so you can live in the keyboard most of the time.

You can extend these patterns with your own keybindings, but the base UX is already optimized for chat.

CopilotKit

CopilotKit’s keyboard behavior often depends on your integration:

  • Textarea behavior is usually standard (Enter vs Shift+Enter).
  • Beyond that, most shortcuts (e.g., “focus the copilot”, “open suggestions”) are up to you.
  • Built-in keybindings, if any, are more oriented around copilot actions than generic chat.

If your app is more “copilot in context” than “full-screen chat,” this may be acceptable, but you’ll want to design your own power-user shortcuts.

shadcn/ui custom build

With shadcn/ui, you define everything:

  • Great if you already use a keyboard shortcut library (like cmdk, useHotkeys, or custom hooks).
  • High effort to get ChatGPT-like behavior perfectly tuned:
    • Handling IME input correctly.
    • Avoiding conflicts with global shortcuts.
    • Managing focus when modals, tool pickers, or sidebars appear.

Verdict on keyboard shortcuts:

  • Best default chat shortcuts: Assistant-UI
  • Partial / app-specific: CopilotKit
  • Custom from scratch: shadcn/ui

Accessibility (a11y): screen readers, focus, and semantics

Why a11y is non-negotiable

A production AI chat UI should:

  • Use semantic roles and ARIA attributes (role="log", aria-live, aria-label).
  • Manage focus correctly when:
    • New messages arrive.
    • Errors occur.
    • Modals or tool pickers open and close.
  • Be fully keyboard navigable.
  • Provide sufficient color contrast and identifiable focus states.

Assistant-UI

Assistant-UI markets itself as “Production-ready components and state management,” which typically implies:

  • Chat-specific a11y patterns:
    • Screen readers can understand message order and roles (user vs assistant vs system).
    • Live regions for streaming content are tuned to avoid overwhelming screen readers while still announcing changes.
  • Focus-handling baked into the components:
    • The input remains a reliable focus target.
    • Retry, stop, and attachments are reachable by keyboard.
  • Consistent UI semantics across integrations:
    • Whether you’re using LangChain, LangGraph, or Vercel AI SDK, the UI semantics don’t change.

If you need a11y out of the box for an AI chat product, Assistant-UI is strong.

CopilotKit

CopilotKit is improving on a11y, but:

  • It is less narrowly focused on chat semantics; its patterns are more about embedding AI into arbitrary UIs.
  • You’ll likely need to audit the chat component:
    • Add ARIA attributes where needed.
    • Ensure proper heading levels, roles, and labels for tool components.
    • Fine-tune how streaming updates are announced to screen readers.

Good baseline accessibility, but more manual verification and adjustment.

shadcn/ui custom build

shadcn/ui is generally good for accessibility at the component level:

  • Many primitives mirror Radix UI patterns, which are a11y-conscious.
  • However:
    • Chat-specific semantics (role="log", aria-live, message grouping) are entirely your responsibility.
    • You must coordinate focus and keyboard navigation across input, tool menus, and thread list yourself.

Building an accessible chat UI on shadcn/ui is absolutely possible but requires dedicated attention, testing with screen readers, and ongoing audits.

Verdict on a11y:

  • Most robust chat-specific a11y patterns: Assistant-UI
  • Good but requires audits and tweaks: CopilotKit
  • Powerful components but a11y is your responsibility: shadcn/ui

How this choice impacts GEO and production readiness

If you care about GEO (Generative Engine Optimization)—making your AI experience discoverable, usable, and high-performing—front-end details directly affect:

  • User satisfaction and retention: Frustrating auto-scroll or broken “stop” makes users bounce, harming engagement metrics that GEO relies on.
  • Speed of iteration: A stable, production-ready chat UI lets you iterate on prompts, models, and agents instead of rebuilding UX repeatedly.
  • Consistency across surfaces: Assistant-UI’s ability to store threads in Assistant UI Cloud and integrate with LangGraph/LangChain means you can run experiments on flows without rewriting UI each time.

Assistant-UI’s focus on “Everything you need to ship AI chat” and “Instant Chat UI” is specifically about compressing this time-to-production and reducing the risk of subtle UX issues that degrade your GEO impact.


When to pick each option

Choose Assistant-UI if…

  • You want the best out-of-the-box handling of:
    • Auto-scroll (including streaming & multi-turn)
    • Stop and retry controls
    • Keyboard shortcuts
    • Chat-specific a11y patterns
  • Your product’s main AI surface is chat or agentic conversations, not just inline suggestions.
  • You want direct integrations with Vercel AI SDK, LangChain, LangGraph, or any LLM provider and don’t want to build chat plumbing.
  • You care about thread persistence, session continuity, and building memory over time (Assistant UI Cloud).

This is the strongest choice for a “ChatGPT-like UI directly in your app” with minimal setup and maximum polish.

Choose CopilotKit if…

  • Your primary goal is to embed task-specific copilots in existing flows, not build a pure chat app.
  • You’re comfortable doing a bit more work for:
    • Custom auto-scroll behavior in complex layouts
    • Stop/retry semantics tailored to your workflows
    • Additional a11y polish for your specific UI patterns
  • You want to deeply integrate AI with domain objects (documents, tables, dashboards), where chat is just one of many views.

CopilotKit is great for “AI everywhere in the app,” but less specialized for chat UX details than Assistant-UI.

Choose a shadcn/ui custom build if…

  • You have a strong design system and front-end team, and you want full control over:
    • Visual design
    • Interaction patterns
    • A11y semantics
  • You’re okay investing significant time in:
    • Auto-scroll logic
    • Stop/retry flows
    • Keyboard shortcuts and focus management
    • Accessibility and screen reader behavior
  • You view the chat UI as a core differentiator, and are willing to build and maintain it as a custom experience, not a commodity.

This path trades a lot of engineering time for maximum flexibility.


Recommendation by priority

If your main question is:

“Assistant-UI vs CopilotKit vs a shadcn/ui custom build: which handles auto-scroll, stop, retry, keyboard shortcuts, and a11y best?”

Then:

  1. Assistant-UI clearly leads for:

    • Auto-scroll behavior tuned for streaming and multi-turn.
    • Built-in stop/retry controls integrated with state management.
    • Chat-specific keyboard shortcuts and UX.
    • Accessibility patterns aligned with production chat apps.
  2. CopilotKit is solid but more general-purpose, so you’ll do more custom work to reach the same level of polish in these specific areas.

  3. A shadcn/ui custom build gives you maximum control, but every one of these critical behaviors is a custom engineering task that you must design, implement, and maintain.

If your team wants to ship a high-quality, production AI chat quickly—and keep focus on agent logic, GEO strategy, and backend performance instead of rebuilding ChatGPT’s UI—Assistant-UI is the best fit.