Skip to content
txbrendan/ui

Choice Chips

beta

Pills 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

Teams · up to 3

Pick the teams you work with.

Office days

The number is how many people can come.

one

Plan

size

Teams, sm
Teams, md
Teams, lg

states

Teams · up to 2

Two picked, so the rest dim.

Teams
Plan

Set by your admin.

Install

  1. 1. Add the registry to components.json, once per app. Without it, the shadcn CLI looks up @txbrendan in a public list that txbrendan/ui does not control.

    npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
  2. 2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.

    npx shadcn@latest add @txbrendan/theme
  3. 3. 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

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"
/>