18 / Agent UI
Suggestions
A row of prompts to start or continue with, for the moment someone does not know what to ask.
Pick one to start.
"use client"
import * as React from "react"
import { FileText, ListChecks, Search } from "lucide-react"
import { Suggestions } from "mischief-ui/suggestions"
const prompts = [
{
id: "summary",
label: "Summarise this document",
icon: <FileText size={14} />,
},
{ id: "risks", label: "Find the risks", icon: <Search size={14} /> },
{
id: "actions",
label: "List the action items",
icon: <ListChecks size={14} />,
},
]
export function SuggestionsDemo() {
const [chosen, setChosen] = React.useState<string | null>(null)
return (
<div className="grid w-full max-w-lg gap-3">
<Suggestions suggestions={prompts} onSelect={(s) => setChosen(s.id)} />
<p className="text-muted-foreground text-center text-xs">
{chosen
? `Chose: ${prompts.find((p) => p.id === chosen)?.label}`
: "Pick one to start."}
</p>
</div>
)
}Installation
Copy the source into your project, or keep it behind a package.
npx shadcn@latest add Tinkerers-Labs/mischief-ui/suggestionsimport { Suggestions } from "mischief-ui/suggestions"Or paste it in yourself. The source imports the shared cn helper from @/lib/utils, so point that at your own copy.
"use client" import * as React from "react"import { cn } from "@/lib/utils" export type Suggestion = { id: string label: React.ReactNode prompt?: string icon?: React.ReactNode} export type SuggestionsProps = Omit< React.HTMLAttributes<HTMLElement>,Usage
const prompts = [
{ id: "summary", label: "Summarise this document" },
{ id: "risks", label: "Find the risks" },
]
export function Starters() {
return <Suggestions suggestions={prompts} onSelect={send} />
}What is shown and what is sent
A suggestion carries a label, which is what people read, and optionally a prompt, which is what you would actually send. They are separate because a good button is short and a good prompt is not: Summarise this reads well on a chip, and does far less than the three sentences you would rather the model receive.
onSelect hands you the whole suggestion, so what you do with it is yours to decide -- send the prompt, or drop it into the composer for editing first.
<Suggestions
suggestions={[
{
id: "summarise",
label: "Summarise this",
prompt: "Summarise the document in five bullets, each one sentence, no preamble.",
},
]}
onSelect={(suggestion) => send(suggestion.prompt ?? String(suggestion.label))}
/>Choosing what to suggest
Suggestions are most useful when someone does not yet know what this thing can do, which means they should show range rather than repeat one idea three ways. Three or four that each open a different door beat eight that all summarise something.
Make them specific to what is actually on screen. Ask about this document earns its place; Ask a question does not, because it tells the reader nothing they had not worked out from the text box.
API
suggestionsSuggestion[]Id, label, and an optional prompt and icon.onSelect(suggestion: Suggestion) => voidRuns with the chosen suggestion.disabledbooleanDisables every suggestion at once.labelstringThe accessible name of the row.Accessibility
The row is a labelled navigation landmark holding a list of buttons, so it can be skipped or entered deliberately rather than being an unlabelled run of controls. It scrolls horizontally with snap points and every target meets the minimum touch size. Nothing is rendered at all when there is nothing to suggest.