# Suggestion Chips

> React suggestion chips for AI chat: prompts people tap instead of typing, as pills under a composer, welcome cards or follow-up rows.

- Type: React component (AI Kit)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add suggestion-chips`
- Docs: https://wingo-ui.com/components/suggestion-chips

## Interaction

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.

## Installation

```bash
npx wingo-ui@latest add suggestion-chips
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/suggestion-chips` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/ui/suggestion-chips.tsx`

npm dependencies: `npm i motion lucide-react`

Wingo UI dependencies (installed automatically):

- [Skeleton](https://wingo-ui.com/components/skeleton) ([Markdown](https://wingo-ui.com/components/skeleton.md))
- [useLocale](https://wingo-ui.com/components/use-locale) ([Markdown](https://wingo-ui.com/components/use-locale.md))
- [useScrollEdges](https://wingo-ui.com/components/use-scroll-edges) ([Markdown](https://wingo-ui.com/components/use-scroll-edges.md))
- [AI Chat](https://wingo-ui.com/components/ai-chat) ([Markdown](https://wingo-ui.com/components/ai-chat.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Number format](https://wingo-ui.com/components/number-format) ([Markdown](https://wingo-ui.com/components/number-format.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
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

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `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. |
| `title?` | `ReactNode` | `""` | An eyebrow above the suggestions, like "Try" or "Continue with". It also names the group for screen readers (labels.group does when it is empty). |
| `showIcons?` | `boolean` | `true` | Shows 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). |
| `showDescriptions?` | `boolean` | `true` | Shows the description under the label on cards (two lines) and rows. Pills never show it. |
| `variant?` | `"chip" \| "card" \| "list"` | `"chip"` | 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. |
| `size?` | `"sm" \| "md" \| "lg"` | `"md"` | 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. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"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. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | 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. |
| `color?` | `string` | `""` | Any CSS color. Replaces the tone for the icons, the tiles and the tint. Leave empty to use the tone. |
| `layout?` | `"auto" \| "wrap" \| "scroll" \| "grid"` | `"auto"` | 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. |
| `columns?` | `2 \| 3 \| 4` | `"2"` | Grid 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. |
| `align?` | `"start" \| "center"` | `"start"` | Where wrapped pills, the eyebrow and the card footer sit. center suits a welcome screen with a centered greeting. |
| `bleed?` | `boolean` | `true` | The 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. |
| `max?` | `number` | `0` | Shows 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. |
| `loading?` | `boolean` | `false` | Skeleton 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. |
| `skeletonCount?` | `number` | `4` | How many skeletons loading shows. |
| `disabled?` | `boolean` | `false` | Dims 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. |
| `animate?` | `boolean` | `true` | Staggers 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) => void` |  | Called 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. |
| `labels?` | `Partial<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. |
| `classNames?` | `Partial<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. |
| `className?` | `string` |  | Extra classes for the root group, merged last. Override the tone vars here, e.g. [--tone-soft-foreground:purple]. |

## Parts

### SuggestionChip (component)

One 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.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `suggestion` | `ChatSuggestion` |  | The suggestion to show: label, prompt, description and icon. |
| `onSelect?` | `(suggestion: ChatSuggestion) => void` |  | Called on a tap or Enter. |
| `variant?` | `"chip" \| "card" \| "list"` | `"chip"` | A pill, a card with a description, or a full-width row. |
| `size?` | `"sm" \| "md" \| "lg"` | `"md"` | Height, padding and type size. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"full"` | Corner shape. |
| `tone?` | `Tone` | `"neutral"` | Semantic color of the icon and the hover tint. |
| `showIcon?` | `boolean` | `true` | Shows the suggestion's icon. |
| `showDescription?` | `boolean` | `true` | Shows the description on cards and rows. |
| `disabled?` | `boolean` | `false` | Dims the chip and blocks it. |

### DEFAULT_SUGGESTION_CHIPS_LABELS (constant)

The English copy: group "Suggestions", refresh "More ideas", more "+3", showMore, showLess and loading. Spread it into labels to translate a few keys.

### ChatSuggestion (type)

One 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.

## See also

- [Tag](https://wingo-ui.com/components/tag) ([Markdown](https://wingo-ui.com/components/tag.md))
- [Button Group](https://wingo-ui.com/components/button-group) ([Markdown](https://wingo-ui.com/components/button-group.md))
- [Empty State](https://wingo-ui.com/components/empty-state) ([Markdown](https://wingo-ui.com/components/empty-state.md))
- [AI Chat](https://wingo-ui.com/components/ai-chat) ([Markdown](https://wingo-ui.com/components/ai-chat.md))
- [Fluid Orb](https://wingo-ui.com/components/fluid-orb) ([Markdown](https://wingo-ui.com/components/fluid-orb.md))

---

Canonical: https://wingo-ui.com/components/suggestion-chips
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
