react ~5 KB 0 deps v0.1.0 ↗ GitHub ↗

@mshafiqyajid/react-chip

Headless chip hook and styled component. Four variants, six tones, selectable, dismissible, icon slots, fully accessible.

Playground #

NeutralPrimarySuccessWarningDangerInfoDismissible
Chip label
Props
TSX
import { ChipStyled } from "@mshafiqyajid/react-chip/styled";
import "@mshafiqyajid/react-chip/styles.css";

<ChipStyled />

Install #

npm install @mshafiqyajid/react-chip

Quick start #

import { ChipStyled } from "@mshafiqyajid/react-chip/styled";
import "@mshafiqyajid/react-chip/styles.css";

<ChipStyled tone="primary">Label</ChipStyled>
<ChipStyled tone="success" variant="solid">Active</ChipStyled>
<ChipStyled tone="danger" dismissible onDismiss={() => {}}>Remove me</ChipStyled>

Selectable chips #

const [selected, setSelected] = useState(false);

<ChipStyled
  tone="primary"
  selectable
  selected={selected}
  onSelect={setSelected}
>
  Toggle me
</ChipStyled>

// Uncontrolled with defaultSelected
<ChipStyled tone="primary" selectable defaultSelected>
  Pre-selected
</ChipStyled>

With icons #

<ChipStyled
  tone="primary"
  icon={<StarIcon />}
  iconRight={<ChevronDownIcon />}
>
  Featured
</ChipStyled>

// Avatar takes priority over icon
<ChipStyled tone="neutral" avatar={<img src="/avatar.jpg" alt="" />}>
  Shafiq Yajid
</ChipStyled>

Headless #

import { useChip } from "@mshafiqyajid/react-chip";

function MyChip({ label }: { label: string }) {
  const { chipProps, dismissProps, isSelected, isDismissed } = useChip({
    selectable: true,
    dismissible: true,
    onSelect: (s) => console.log("selected:", s),
    onDismiss: () => console.log("dismissed"),
  });

  if (isDismissed) return null;

  return (
    <span {...chipProps} className="my-chip" data-selected={isSelected || undefined}>
      {label}
      <button {...dismissProps}>×</button>
    </span>
  );
}

API #

PropTypeDefaultDescription
variant"solid" | "subtle" | "outline" | "soft""subtle"Visual style
tone"neutral" | "primary" | "success" | "warning" | "danger" | "info""neutral"Color tone
size"sm" | "md" | "lg""md"Size scale
selectablebooleanfalseEnable selection toggle; adds role="option" and keyboard support
selectedboolean—Controlled selected state
defaultSelectedbooleanfalseUncontrolled initial selected state
onSelect(selected: boolean) => void—Called when selection changes
dismissiblebooleanfalseShow dismiss button with aria-label="Remove"
onDismiss() => void—Called when chip is dismissed
iconReactNode—Leading icon
iconRightReactNode—Trailing icon
avatarReactNode—Leading avatar element (takes priority over icon)
disabledbooleanfalseDisable all interactions
childrenReactNode—Chip label (required)
classNamestring—Extra class on the root element
styleCSSProperties—Inline style override

Headless API — useChip #

OptionTypeDefaultDescription
selectablebooleanfalseEnable selection behavior
selectedboolean—Controlled selected state
defaultSelectedbooleanfalseInitial uncontrolled selected state
onSelect(selected: boolean) => void—Called on selection change
dismissiblebooleanfalseEnable dismiss behavior
onDismiss() => void—Called when dismissed
disabledbooleanfalseDisable all interactions

Returns chipProps, dismissProps, isSelected, isDismissed, select(v?), and dismiss(). Dismissed chips return isDismissed=true — render null in your component to remove them from the DOM.

Edit this page on GitHub