Render runtime-generated follow-up prompt chips after an assistant response.
ThreadFollowupSuggestions renders the current thread's runtime suggestions as clickable prompt chips. Use it for follow-up prompts that arrive after a response, such as suggested next questions, refinements, or task shortcuts.
This component reads from thread.suggestions. For static welcome-screen prompts, use ThreadPrimitive.Suggestions instead.
Getting Started
Add follow-up-suggestions
npx shadcn@latest add @assistant-ui/follow-up-suggestionsThe @assistant-ui namespace resolves the Radix or Base UI flavor from your project's style through the style-aware registry entry in components.json. Without that entry, add by direct URL instead:
npx shadcn@latest add https://r.assistant-ui.com/base/follow-up-suggestions.jsonMain Component
npm install @assistant-ui/react"use client";import { AuiIf, useAuiState, ThreadPrimitive } from "@assistant-ui/react";import { useCallback, useEffect, useRef, useState, type FC } from "react";const FollowupSuggestionsRow: FC = () => { const suggestions = useAuiState((s) => s.thread.suggestions); const scrollRef = useRef<HTMLDivElement>(null); const rtlRef = useRef<boolean | null>(null); const [fades, setFades] = useState({ left: false, right: false }); const updateFades = useCallback(() => { const el = scrollRef.current; if (!el) return; const maxScroll = el.scrollWidth - el.clientWidth; // scrollLeft runs 0..-max in RTL; normalize to hidden width per physical edge. const fromStart = Math.abs(el.scrollLeft); // getComputedStyle forces a style recalc per scroll event; direction is stable, read it once. const rtl = (rtlRef.current ??= getComputedStyle(el).direction === "rtl"); const [left, right] = rtl ? [maxScroll - fromStart, fromStart] : [fromStart, maxScroll - fromStart]; setFades((prev) => { const next = { left: left > 1, right: right > 1 }; return prev.left === next.left && prev.right === next.right ? prev : next; }); }, []); useEffect(() => { updateFades(); const el = scrollRef.current; if (!el?.firstElementChild) return undefined; const observer = new ResizeObserver(updateFades); observer.observe(el); observer.observe(el.firstElementChild); return () => observer.disconnect(); }, [updateFades]); const maskImage = `linear-gradient(to right, ${ fades.left ? "transparent, black 2rem" : "black" }, ${fades.right ? "black calc(100% - 2rem), transparent" : "black"})`; return ( <div ref={scrollRef} onScroll={updateFades} // overflow-x clips both axes; py-1/-my-1 gives focus rings vertical room without changing outer height. className="aui-thread-followup-suggestions -my-1 w-full overflow-x-auto py-1 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden" style={{ maskImage, WebkitMaskImage: maskImage }} > <div className="mx-auto flex min-h-8 w-max items-center gap-2 px-0.5"> {suggestions.map((suggestion, idx) => ( <ThreadPrimitive.Suggestion key={idx} className="aui-thread-followup-suggestion bg-background hover:bg-muted/80 rounded-full border px-3 py-1 text-sm whitespace-nowrap transition-colors ease-in" prompt={suggestion.prompt} method="replace" autoSend > {suggestion.prompt} </ThreadPrimitive.Suggestion> ))} </div> </div> );};export const ThreadFollowupSuggestions: FC = () => ( <AuiIf condition={(s) => !s.thread.isEmpty && !s.thread.isRunning && s.thread.suggestions.length > 0 } > <FollowupSuggestionsRow /> </AuiIf>);This adds a /components/assistant-ui/follow-up-suggestions.tsx file to your project. The default thread component already renders ThreadFollowupSuggestions, so installing it standalone is only needed when you build your own thread layout.
Provide runtime suggestions
Pass suggestions through your runtime. External-store runtimes, AI SDK runtimes, and local runtimes can all surface follow-up prompts through thread.suggestions.
const runtime = useExternalStoreRuntime({
messages,
convertMessage,
onNew: async (message) => {
// append the message in your store
},
suggestions: [
{ prompt: "Summarize this as action items" },
{ prompt: "Write a shorter version" },
],
});Render after messages
Place ThreadFollowupSuggestions near the bottom of your thread viewport, after the assistant message list and before the composer.
import { ThreadFollowupSuggestions } from "@/components/assistant-ui/follow-up-suggestions";
const ThreadViewportFooter = () => {
return (
<ThreadPrimitive.ViewportFooter>
<ThreadPrimitive.ScrollToBottom />
<ThreadFollowupSuggestions />
<Composer />
</ThreadPrimitive.ViewportFooter>
);
};Behavior
The component only renders when the thread is not empty, not currently running, and has at least one suggestion. Each suggestion uses ThreadPrimitive.Suggestion, replaces the composer text with the prompt, and sends it immediately.
Suggestions stay on a single line. When they overflow, the row scrolls horizontally without a scrollbar, and each edge with clipped content fades out — so no fade shows at the start edge until you scroll.
Related Components
- Thread - Complete chat interface with message list and composer
- Suggestions guide - Runtime and static suggestion patterns