Follow-up Chips
One-tap next steps the agent offers after an answer.
≈ 543 tokens per use (tool 289 · guidance 212 · typical payload 42)How token costs are estimated
Example: Follow-up Chips
Give me a plan for a Sunday lamb roast. I'm still figuring out numbers.
When to use
- You finished an answer or a task, and there are 2–4 likely, specific next steps.
- The user may not know about a useful thing you can do next, like adjusting a plan or exporting results.
When not to use
- The user already said what they want next.
- The only suggestions you have are filler, like "Tell me more" or "Anything else?".
- You need an answer before you can continue. Use a Question Stepper.
- A task is waiting for approval. Finish that first.
Writing suggestions
| Do | Don't |
|---|---|
| Adjust the plan for 5 guests | Would you like me to adjust the plan? |
| Copy these changes to Google Ads | More options |
| Does it work with oat milk? | Tell me more |
Write each label as the user's own request, under 60 characters, and only offer what you can actually do. Use prompt when the message you need is longer than the label.
Layouts
chips(default) for short actions.listfor longer, question-like suggestions, like a "Related" section.
Suggestions can be tapped as soon as they stream in. After a tap, the chosen one stays marked and the others dim, so the chat history shows what was picked.
Installation
npx shadcn@latest add https://your-site.com/r/follow-up-chips.json
Usage
import { tool } from "ai"
import { followUpChipsTool } from "@/components/genui/follow-up-chips.schema"
const tools = {
// No execute(): the call is completed by the user's tap in the UI.
[followUpChipsTool.name]: tool({
description: followUpChipsTool.description,
inputSchema: followUpChipsTool.inputSchema,
}),
}import { FollowUpChips } from "@/components/genui/follow-up-chips"
function ToolPart({ part, addToolResult }) {
if (part.type !== "tool-suggest_follow_ups") return null
return (
<FollowUpChips
{...part.input}
status={part.state === "input-streaming" ? "streaming" : undefined}
result={part.state === "output-available" ? part.output : undefined}
onResult={(result) =>
addToolResult({
tool: "suggest_follow_ups",
toolCallId: part.toolCallId,
output: result,
})
}
/>
)
}The agent receives:
{ action: "select", id: "guests", prompt: "Adjust the plan for 5 guests" }Treat prompt exactly as if the user had typed it. Many apps also send it as a visible user message, which is what the user expects to see.
With LangGraph
Push it from a graph node with ui.push({ name: "suggest_follow_ups", props: { toolCallId, ...args } }, { message }), and render the thread's UI messages with the Renderer.
Restoring from history
When a conversation is reloaded, pass the stored result back as result. The component renders its finished state (the chosen suggestion) and never calls onResult again, so a decision can't be made twice. With the AI SDK, that's the tool call's output:
<FollowUpChips
{...part.input}
result={part.state === "output-available" ? part.output : undefined}
/>Schema
| Prop | Type | Notes |
|---|---|---|
label | string? | A short group label such as "Related". Leave it out when the chips speak for themselves |
suggestions | { id, label, prompt? }[] | 1–4. label is at most 80 characters |
layout | "chips" | "list" | Default chips |
Host props: status (streaming, ready, selected), onResult, result and className.