Skip to content

Chip component

Chip is a compact pill for a tag, a status label, or one item in a set of selectable filters. It covers three related but distinct jobs: a plain static label, a closable tag with its own dismiss button, and a selectable/toggleable chip for filter-style UI — which one it is depends entirely on which props you pass.

Data Display19 propsAccessibility notes

Playground

Change a prop on the right and the preview updates. Switch to Code to copy what you see.

Recipes

Copy-paste patterns for the ways Chip actually gets used — each one a real, self-contained example.

Variants

Four weights — solid, soft, bordered, and a dot variant for status labels.

<Chip>Solid</Chip>
<Chip variant="soft">Soft</Chip>
<Chip variant="bordered">Bordered</Chip>
<Chip variant="dot">Dot</Chip>

Semantic colors

Soft chips make great status pills — colour them by meaning (paid, pending, failed).

<Chip variant="soft" color="success">Paid</Chip>
<Chip variant="soft" color="warning">Pending</Chip>
<Chip variant="soft" color="danger">Failed</Chip>
<Chip variant="soft" color="primary">New</Chip>

With an icon

startContent puts an icon before the label — handy for tags and categories.

<Chip variant="soft" startContent={<TagIcon />}>Design</Chip>
<Chip variant="soft" color="primary" startContent={<CheckIcon />}>
  Verified
</Chip>

Closable (removable)

Pass onClose and Chip shows a × button — the building block of a filter/token input. Track the list in state and drop the chip on close.

const [tags, setTags] = useState(["Design", "Dev", "QA"]);

{tags.map((t) => (
  <Chip key={t} variant="soft" onClose={() => setTags(tags.filter((x) => x !== t))}>
    {t}
  </Chip>
))}

With an avatar

The avatar prop tucks a small image at the chip's start — a person or entity token.

<Chip variant="bordered" avatar={<Avatar src="/hero/person-woman.jpg" name="Sara" />}>
  Sara Whitfield
</Chip>

Plain and closable chips

With no onClose, Chip is a static, non-interactive label — for status tags ("Active", "Pending") that aren't meant to be dismissed. Pass onClose and Chip grows a close button automatically; onClose fires when it's pressed, and removing the chip from your own state list is on you.

<Chip color="success">Active</Chip>

<Chip onClose={() => removeTag(tag.id)} color="default">
  {tag.label}
</Chip>

Selectable chips

Pass selected (controlled) or defaultSelected (uncontrolled) and Chip becomes a real toggleable control: role="button", aria-pressed reflecting the state, and both click and Enter/Space toggling it. Reach for this instead of onClose for a filter-style multi-select set of chips.

const TagFilter = ({ tags }: { tags: string[] }) => {
  const [active, setActive] = useState<Set<string>>(new Set());
  return (
    <div className="flex flex-wrap gap-2">
      {tags.map((tag) => (
        <Chip
          key={tag}
          selected={active.has(tag)}
          onSelectedChange={(isSelected) => {
            setActive((prev) => {
              const next = new Set(prev);
              isSelected ? next.add(tag) : next.delete(tag);
              return next;
            });
          }}
          variant="bordered"
        >
          {tag}
        </Chip>
      ))}
    </div>
  );
};

Avatar and content slots

avatar renders at the very start of the chip (ahead of startContent), for a chip representing a person or entity with an image next to the label — a common pattern for a "tagged people" list. startContent/endContent sit around the label itself for anything else — an icon, a small count.

<Chip avatar={<Avatar size="xs" src={user.avatarUrl} />} onClose={() => untag(user.id)}>
  {user.name}
</Chip>

Variants

variant="dot" is a distinct fourth option alongside solid/soft/bordered — it renders a small colored dot ahead of the label instead of tinting the whole chip, a lighter-weight way to convey the same semantic color without as much visual weight.

<Chip variant="dot" color="success">Online</Chip>

Props

The full public API for Chip. Every prop is stable and intentionally typed.

Content

children

ReactNode

default: —

Chip label content.

avatar

ReactNode

default: —

Avatar rendered at the start of the chip.

startContent

ReactNode

default: —

Content rendered before the label.

endContent

ReactNode

default: —

Content rendered after the label, before the close button.

closeIcon

ReactNode

default: —

Overrides the default × close icon.

Appearance

variant

"solid" | "soft" | "bordered" | "dot"

default: solid

Visual style variant.

shadow

"none" | "xs" | "xs-sm" | "sm" | "md" | "lg"

default: none

Box-shadow depth.

size

"xs" | "xs-sm" | "sm" | "md" | "lg" | "xl"

default: md

Chip size.

color

"default" | "primary" | "secondary" | "info" | "success" | "warning" | "danger"

default: default

Semantic color.

radius

"none" | "xs" | "xs-sm" | "sm" | "md" | "lg" | "full"

default: full

Border radius.

colorDepth

50 | 100 | ... | 950

default: 500

Shade depth for the selected color.

Behavior & states

isDisabled

boolean

default: false

Disables interaction.

selected

boolean

default: —

Controlled selected state; renders as a toggleable role="button" chip.

defaultSelected

boolean

default: —

Uncontrolled initial selected state.

Events

onClose

(e: PressEvent) => void

default: —

Shows a close button and is called when it's pressed.

onSelectedChange

(selected: boolean) => void

default: —

Called when selected state changes.

Styling

classNames

Partial<Record<ChipSlot, string>>

default: —

Per-slot class overrides (base/content/dot/avatar/closeButton/endContent).

className

string

default: —

Class applied to the root element.

style

CSSProperties

default: —

Inline styles for the root element.

Accessibility

  • Only chips with selected/defaultSelected get button semantics (role="button", aria-pressed, keyboard activation) — a plain or closable chip with no selection state is not independently interactive itself, only its close button is.
  • The close button rendered by onClose is a real, keyboard-operable button with its own accessible name, separate from the chip's own label.
  • For a selectable chip, aria-pressed reflects the actual selected state, so assistive technology announces the toggle correctly on every change, not just visually.

Best practices

  • Don't combine onClose and selected on the same chip — closable tags and selectable filter chips are different interaction patterns, and stacking both on one chip makes it unclear what a click actually does.
  • Use variant="dot" for a dense list of status labels where a full-color pill per item would be visually heavy.
  • For filter-style chip groups, drive selected from your own state (as in the TagFilter example) rather than relying on defaultSelected — you'll almost always need to read the current selection elsewhere (to filter a list, to show a count).
  • Keep chip labels to a word or two; a chip stretching across multiple lines defeats the compact, scannable point of the component.