Suggestion Chips React component for Tailwind CSS v4
v1.0.0Added in 1.0.0
React suggestion chips for AI chat: prompts people tap instead of typing, as pills under a composer, welcome cards or follow-up rows.
Tap or click a suggestion and onSelect hands it to the app, which sends or inserts its prompt; nothing stays selected. Pills and cards dip on a quick spring when pressed and tint on hover; follow-up rows tint and nudge their arrow. On a phone the pills (below 768px) and the cards (below 640px) sit in one row that swipes, snaps per item, runs to the screen edges and fades where more is waiting, with 44px targets; on a desktop they wrap or form a grid. With max set, a +N pill (or Show more under cards and rows) reveals the rest in place and turns into Show less; focus moves to the first revealed suggestion. More ideas spins its icon until your onRefresh promise settles, then only the new suggestions stagger in. The whole group is one Tab stop: Left and Right move between pills and cards (Up and Down between rows of a wrapped or grid layout, and through the list variant), Home and End jump to the ends, Enter or Space picks. While a reply streams, disabled dims everything and blocks picks but keeps focus where it was. Reduced motion turns the stagger and slides into short fades.
Suggestion Chips is a Pro React component in the React AI components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add suggestion-chips, together with Skeleton, useLocale, useScrollEdges, AI Chat, Motion presets and 3 more and the npm packages motion and lucide-react.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add suggestion-chipsSuggestion Chips is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import SuggestionChips from @/components/ui/suggestion-chips.
import { CalendarDays, ChartColumn, Mail, Receipt } from "lucide-react"
import { SuggestionChip, SuggestionChips, type ChatSuggestion } from "@/components/ui/suggestion-chips"
const suggestions: ChatSuggestion[] = [
{ id: "overdue", label: "Summarize overdue invoices", description: "What is left to collect this month", icon: <Receipt /> },
{ id: "follow-up", label: "Write a follow-up email", description: "For offer #OF-2026-118", icon: <Mail /> },
{ id: "q3", label: "Compare Q3 collections", description: "July, August and September", icon: <ChartColumn /> },
{ id: "meeting", label: "Prepare Monday's meeting", description: "Agenda and the week's numbers", icon: <CalendarDays /> },
]
export function WelcomeSuggestions({ onSend }: { onSend: (text: string) => void }) {
return (
<SuggestionChips
variant="card"
title="Try"
suggestions={suggestions}
onSelect={(suggestion) => onSend(suggestion.prompt ?? suggestion.label)}
/>
)
}
// one pill, card or row in your own layout: same look, press and tooltip, without the roving focus
export function PinnedSuggestion({ onSend }: { onSend: (text: string) => void }) {
return <SuggestionChip variant="card" suggestion={suggestions[0]} onSelect={(s) => onSend(s.prompt ?? s.label)} />
}Props
suggestions*ChatSuggestion[]The suggestions, keyed by id, in order: { id, label, prompt?, description?, icon?, group? } (ChatSuggestion from ai-chat, re-exported here). prompt is what the app should send, defaulting to label. An empty list renders nothing unless loading. Long labels truncate (pill) or clamp to two lines (card) and show the full text as a tooltip.
titledefault""ReactNodeAn eyebrow above the suggestions, like "Try" or "Continue with". It also names the group for screen readers (labels.group does when it is empty).
showIconsdefaulttruebooleanShows each suggestion's icon: before the label on a pill, in a tinted tile on a card (a Sparkles when the suggestion has none), and as the leading glyph of a row (a reply arrow when it has none).
showDescriptionsdefaulttruebooleanShows the description under the label on cards (two lines) and rows. Pills never show it.
variantdefault"chip""chip" | "card" | "list"chip: pills, the classic row under an empty composer. card: tiles with an icon, a label and a two-line description for a new chat's welcome screen. list: full-width rows with a trailing arrow, for follow-ups under an answer or a prompt library.
sizedefault"md""sm" | "md" | "lg"Pills 32 / 36 / 40px tall with 13 / 14 / 15px text (44px on touch screens), cards at least 64 / 76 / 88px tall and 216 / 240 / 264px wide in a rail, rows 40 / 44 / 48px.
radiusdefault"full""none" | "sm" | "md" | "lg" | "full"Corner shape. Pills use the button's radius per size (full is a pill), cards the surface radius (12 / 16 / 24px, full is 24px) with the icon tile following along, rows the pill radius capped at 12px.
tonedefault"neutral""neutral" | "brand" | "success" | "warning" | "danger" | "info"Colors the icons (and card tiles) and the hover and pressed tint. neutral keeps icons muted and tints with the foreground; the pills themselves always stay a calm gray.
colordefault""stringAny CSS color. Replaces the tone for the icons, the tiles and the tint. Leave empty to use the tone.
layoutdefault"auto""auto" | "wrap" | "scroll" | "grid"auto: pills swipe in one row below 768px and wrap above; cards swipe below 640px and form a grid above. wrap, scroll and grid force one arrangement at every width. The list variant always stacks.
columnsdefault"2"2 | 3 | 4Grid columns from 640px. A narrower container drops columns so a card never gets squeezed below about 170px: three columns need 544px of room, four need 736px.
aligndefault"start""start" | "center"Where wrapped pills, the eyebrow and the card footer sit. center suits a welcome screen with a centered greeting.
bleeddefaulttruebooleanThe swiping row runs 16px into the page gutter, so items slide off the screen edge instead of stopping at a hard cut; list rows reach 8px into it, so their text lines up with the eyebrow while the hover highlight extends outward. Turn it off inside a card or a panel without that much padding.
maxdefault0numberShows at most this many; the rest wait behind a +N pill (chip) or Show more (card, list) that reveals them in place and turns into Show less. 0 shows all, and a max at or above the count shows no +N.
loadingdefaultfalsebooleanSkeleton pills, cards or rows in the variant's exact shape while suggestions load; the group is aria-busy and says so to screen readers once.
skeletonCountdefault4numberHow many skeletons loading shows.
disableddefaultfalsebooleanDims every suggestion to 50% and blocks picks, for while a reply streams. Items stay focusable (aria-disabled), so keyboard focus is never thrown back to the page.
animatedefaulttruebooleanStaggers suggestions in (40ms apart, all in within 0.3s) when they mount in the browser and when the set changes; suggestions that stay keep their place. Server-rendered html already shows the final state. Reduced motion fades without offsets.
onSelect(suggestion: ChatSuggestion) => voidCalled with the picked suggestion; send or insert suggestion.prompt ?? suggestion.label. Not called while disabled. The component keeps no selection.
onRefresh() => (void | Promise<unknown>)Adds a trailing More ideas pill (or row, or a button under cards). Its icon spins until the returned promise settles and always finishes the turn it is on; then pass the new suggestions and only the new ids stagger in.
labelsPartial<SuggestionChipsLabels>Every string, merged over the English defaults: group "Suggestions" (the group's name without a title), refresh "More ideas", more (count, formatted) => "+3", showMore "Show more", showLess "Show less", loading "Loading suggestions". The count is formatted in the UIProvider locale.
classNamesPartial<Record<"rail" | "chip" | "icon" | "label" | "description" | "arrow" | "title" | "more" | "refresh" | "footer", string>>Extra classes for the parts: the rail (the scroller, wrap or grid), every chip, card or row, their icon, label, description and trailing arrow, the eyebrow, the +N and refresh controls, and the footer under cards.
classNamestringExtra classes for the root group, merged last. Override the tone vars here, e.g. [--tone-soft-foreground:purple].
Parts
SuggestionChip
componentOne suggestion on its own, for a single starter card or a chip placed by hand. Same look and press as the chips in the group.
suggestion*ChatSuggestionThe suggestion to show: label, prompt, description and icon.
onSelect(suggestion: ChatSuggestion) => voidCalled on a tap or Enter.
variantdefault"chip""chip" | "card" | "list"A pill, a card with a description, or a full-width row.
sizedefault"md""sm" | "md" | "lg"Height, padding and type size.
radiusdefault"full""none" | "sm" | "md" | "lg" | "full"Corner shape.
tonedefault"neutral"ToneSemantic color of the icon and the hover tint.
showIcondefaulttruebooleanShows the suggestion's icon.
showDescriptiondefaulttruebooleanShows the description on cards and rows.
disableddefaultfalsebooleanDims the chip and blocks it.
DEFAULT_SUGGESTION_CHIPS_LABELS
constantThe English copy: group "Suggestions", refresh "More ideas", more "+3", showMore, showLess and loading. Spread it into labels to translate a few keys.
ChatSuggestion
typeOne suggestion, shared with the AI chat kit (lib/ai-chat): id, label, prompt (defaults to label), description, icon and group.
Examples
Welcome cards
variant="card" columns={3}: three across with room to spare, two in a narrow box like this one, a swipe rail on phones.
Follow-ups
variant="list" under an answer. Picking one disables the rows while the reply streams.
Phone rail
layout="scroll" in a 390px frame: the rail bleeds to the edges, snaps per chip and fades where more is waiting.
Refresh
onRefresh returns a promise; the icon spins until it settles and finishes its turn.
Tones and custom color
Brand icons on calm gray pills, an info-toned card grid and a violet follow-up list.
Overflow and loading
max={3} folds the rest behind +3; loading shows skeletons in each variant's shape.
Sizes and radius
32, 36 and 40px pills (44px on touch) with the button's radius scale.
SuggestionChip in your own layout
The same pill and card one at a time, with tone and disabled per chip.
Dependencies
Files
- components/ui/suggestion-chips.tsx
FAQ
Is the React Suggestion Chips component free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Suggestion Chips?
Run npx wingo-ui@latest add suggestion-chips. It also adds the Wingo UI items it builds on (Skeleton, useLocale, useScrollEdges, AI Chat, Motion presets and 3 more) and the npm packages motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/suggestion-chips.
How does Suggestion Chips behave on phones?
On a phone the pills (below 768px) and the cards (below 640px) sit in one row that swipes, snaps per item, runs to the screen edges and fades where more is waiting, with 44px targets; on a desktop they wrap or form a grid.
See also
Used in
More React AI components
Articles using this component
- React Prompt Input: Enter, IME, Files, Stop and Voice
Building UI with AI agents10 min read