Choice Chips
betaPills for short tags, like teams, plans, and days of the week. Pick several by default, with a check that springs in, or pick one with the arrow keys.
Preview
several
one
size
states
Install
1. Add the registry to
components.json, once per app. Without it, the shadcn CLI looks up@txbrendanin a public list that txbrendan/ui does not control.npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.
npx shadcn@latest add @txbrendan/theme3. Add the component.
npx shadcn@latest add @txbrendan/choice-chips
Use it for
- Short tags only: teams, plans, days of the week. One or two words each.
- Pick several by default. Each chip is a checkbox, a Tab stop of its own, and Space ticks it.
- Pass multiple={false} to pick one. The chips become a radio group: one Tab stop, and the arrows move and choose.
- In a single choice, pressing the chosen chip again clears it, so the value can be null.
- Set max to cap how many can be picked. Say the cap in the label, like "Teams · up to 3". The rest dim once it is reached.
- Set error when the form is sent with nothing picked, like "Pick at least one team".
Avoid it for
- An option with a hint, a price, or a consequence. Use a selection card.
- A long list, or one people search. Use a combobox.
- Turning one setting on or off. Use a switch.
- Free tags people make up. These chips only offer the options you pass.
Props
- options
The chips. A string is its own id and label. count shows a muted number after the label.
Array<string | { id: string; label: string; count?: number }>
- multiple
Pick several, as checkboxes. false picks one, as a radio group, and value becomes one id or null.
boolean · default true
- value
The ids picked: an array, or one id or null when multiple is false. Use it with onValueChange.
string[] | string | null
- defaultValue
The ids picked at first render, when you do not control them.
string[] | string | null
- onValueChange
Called with the ids picked, or with one id or null when multiple is false.
(value: string[] | string | null) => void
- max
Most that can be picked. Once it is reached, the rest dim and do nothing when pressed.
number
- label
Names the group, above it. Pressing it focuses the group's Tab stop.
ReactNode
- hint
A short note under the chips. It describes the group.
ReactNode
- error
Replaces the hint, in red, rings the open chips in red, and is announced once.
ReactNode
- size
Chip height and text size: 28, 32, or 36px at the default density.
"sm" | "md" | "lg" · default "md"
- disabled
Blocks every chip, and dims the label.
boolean · default false
- className
Classes for the frame. id, ref, and aria-* land on the group.
string
| Prop | Type | Default | What it does |
|---|---|---|---|
| options | Array<string | { id: string; label: string; count?: number }> | - | The chips. A string is its own id and label. count shows a muted number after the label. |
| multiple | boolean | true | Pick several, as checkboxes. false picks one, as a radio group, and value becomes one id or null. |
| value | string[] | string | null | - | The ids picked: an array, or one id or null when multiple is false. Use it with onValueChange. |
| defaultValue | string[] | string | null | - | The ids picked at first render, when you do not control them. |
| onValueChange | (value: string[] | string | null) => void | - | Called with the ids picked, or with one id or null when multiple is false. |
| max | number | - | Most that can be picked. Once it is reached, the rest dim and do nothing when pressed. |
| label | ReactNode | - | Names the group, above it. Pressing it focuses the group's Tab stop. |
| hint | ReactNode | - | A short note under the chips. It describes the group. |
| error | ReactNode | - | Replaces the hint, in red, rings the open chips in red, and is announced once. |
| size | "sm" | "md" | "lg" | "md" | Chip height and text size: 28, 32, or 36px at the default density. |
| disabled | boolean | false | Blocks every chip, and dims the label. |
| className | string | - | Classes for the frame. id, ref, and aria-* land on the group. |
Accessibility
- Several answers: a group of checkboxes. Each chip is a Tab stop, and Space ticks it.
- One answer: a radio group. Tab lands on the chosen chip, or the first. The arrows move and choose, and wrap. Home and End jump to the ends.
- The label names the group, and the hint or error describes it. An error is announced once when it appears.
- Once max is reached, the rest are aria-disabled, so they stay findable with the keys.
- Focusing the group itself, as a form does with its first error, moves focus to its Tab stop.
- A chosen chip shows by its fill and, with several answers, a check. In forced-colors mode, where fills are gone, a single answer shows the check too.
Examples
Several answers
<ChoiceChips
label="Teams · up to 3"
options={["Design", "Engineering", "Support", "Marketing"]}
defaultValue={["Engineering"]}
max={3}
/>One answer
<ChoiceChips
multiple={false}
label="Plan"
options={["Starter", "Team", "Business", "Scale", "Enterprise"]}
defaultValue="Team"
/>