oks-ui

How to use

Chip

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.

import { Chip } from "oks-ui";

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.

function 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>

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.

Props reference

All available props for Chip. See the full component page for an interactive playground.

Props

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.

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.
View full API reference for Chip