Chip

Compact tag with an optional dense remove button.

moleculesSource
import { Chip } from '@elirobinson/react/components/molecules/Chip';

Styles: @elirobinson/react/styles/molecules/Chip.css — already included when you import @elirobinson/react/styles.css.

CoachingRecipesAI consulting
Show code
import { Chip } from '@elirobinson/react/components/molecules/Chip';

export default function Basic() {
  return (
    <div className="demo-row">
      <Chip>Coaching</Chip>
      <Chip>Recipes</Chip>
      <Chip>AI consulting</Chip>
    </div>
  );
}

When to use it

Use Chip for a compact, removable tag — an active filter, a selected item in a multi-select field, a keyword attached to a piece of content. Without onRemove it's just a static tag; with it, a small × button appears. For a status label that's never removable, Badge is the simpler component.

Removable

Pass onRemove to get the dense remove button. It's sized to the shadcn/MUI scale, not the 44px primary-control minimum — a chip is an inline, dense affordance, not a button.

Practice plansU10SoccerFall season
Show code
import { useState } from 'react';

import { Chip } from '@elirobinson/react/components/molecules/Chip';

export default function Removable() {
  const [tags, setTags] = useState(['Practice plans', 'U10', 'Soccer', 'Fall season']);

  const removeTag = (tag: string) => {
    setTags((current) => current.filter((item) => item !== tag));
  };

  return (
    <div className="demo-row">
      {tags.length === 0 ? (
        <p>No filters applied.</p>
      ) : (
        tags.map((tag) => (
          <Chip key={tag} onRemove={() => removeTag(tag)}>
            {tag}
          </Chip>
        ))
      )}
    </div>
  );
}

Props

PropTypeDefaultDescription
onRemove(() => void)
removeLabelstringAccessible name for the remove button. Defaults to `Remove ${children}`, which only produces a correct label when `children` is a plain string. Pass this explicitly when `children` is an element (e.g. `<b>Design</b>`).

Also accepts all HTMLAttributes<HTMLSpanElement> props.

Accessibility

  • Renders a <span> wrapper around a label span and, when onRemove is passed, a native <button type="button"> for removal — everything else about the chip is non-interactive.
  • The remove button's accessible name defaults to `Remove ${children}`. That only produces a correct label when children is a plain string — pass removeLabel explicitly when the content is an element (<b>Design</b> would otherwise stringify wrong).
  • The remove button follows dense inline sizing (MUI/shadcn scale), not the 44px primary-control minimum, since a chip's own footprint is already small.
  • Keyboard: the remove button is a native <button>Tab reaches it, Enter and Space activate it. The chip body itself isn't focusable.
  • The ref forwards to the outer <span>.

Do

  • Keep chip labels to a word or short phrase — a filter name, a tag, a selected value.
  • Pass removeLabel when the chip content is anything other than plain text.
  • Update surrounding state (a filter list, a form value) synchronously in onRemove.
  • Use Chip for values the user actively chose and can un-choose.

Don't

  • Use Chip for a status that isn't removable — that's Badge.
  • Rely on the expanded hit area overlapping the label — the remove button's target is bounded to its own footprint.
  • Put more than a handful of chips in a row without wrapping — it turns into a wall of tags.
  • Make the whole chip clickable for one action and removable for another; keep each chip to a single control.