Skip to content
LuoForge/Tungsten
Components/Chips·

Chips

since v4.3.0

Form input where the user picks one or more values from a small list,

rendered as pill-shaped toggle buttons. Common in onboarding, filter UIs,

and preference settings.

Why a dedicated primitive: Radio is single-select with radio chrome;

Checkbox is multi-select with checkbox chrome; PillNav carries navigation

semantics. Chips uses aria-pressed toggle buttons in a role="group",

which matches what screen readers expect for a pill-shaped selection list.

Keyboard: Tab focuses the first chip; Arrow Left/Right traverses (wrapping,

skipping disabled chips); Space/Enter toggles.

Forwards ref to the root <div role="group">.

Install

Add the package and import the component.

pnpm add @hey-mike/tungsten
pnpm add @hey-mike/tungsten
import { Chips } from '@hey-mike/tungsten';
import { Chips } from '@hey-mike/tungsten';

Preview

Same fixtures used by the visual-regression suite.

Usage

apps/docs/app/snapshots/chips/page.tsx

'use client';

import { useState } from 'react';
import { Chips } from '@hey-mike/tungsten';
import { VariantGrid } from '../_components/VariantGrid';

function SingleSelect() {
  const [value, setValue] = useState('react');
  return (
    <Chips
      options={['react', 'vue', 'svelte']}
      value={value}
      onChange={setValue}
    />
  );
}

function MultiSelect() {
  const [value, setValue] = useState<string[]>(['react', 'svelte']);
  return (
    <Chips
      multiple
      options={['react', 'vue', 'svelte', 'angular']}
      value={value}
      onChange={setValue}
    />
  );
}

function WithLabel() {
  const [value, setValue] = useState('intermediate');
  return (
    <Chips
      label="Difficulty"
      options={['beginner', 'intermediate', 'advanced']}
      value={value}
      onChange={setValue}
    />
  );
}

function WithDisabled() {
  const [value, setValue] = useState('react');
  return (
    <Chips
      options={[
        { value: 'react', label: 'react' },
        { value: 'vue', label: 'vue', disabled: true },
        { value: 'svelte', label: 'svelte' },
      ]}
      value={value}
      onChange={setValue}
    />
  );
}

export default function ChipsSnapshot() {
  return (
    <VariantGrid
      title="Chips"
      variants={[
        { label: 'single-select', node: <SingleSelect /> },
        { label: 'multi-select', node: <MultiSelect /> },
        { label: 'with-label', node: <WithLabel /> },
        { label: 'with-disabled', node: <WithDisabled /> },
      ]}
    />
  );
}
'use client';

import { useState } from 'react';
import { Chips } from '@hey-mike/tungsten';
import { VariantGrid } from '../_components/VariantGrid';

function SingleSelect() {
  const [value, setValue] = useState('react');
  return (
    <Chips
      options={['react', 'vue', 'svelte']}
      value={value}
      onChange={setValue}
    />
  );
}

function MultiSelect() {
  const [value, setValue] = useState<string[]>(['react', 'svelte']);
  return (
    <Chips
      multiple
      options={['react', 'vue', 'svelte', 'angular']}
      value={value}
      onChange={setValue}
    />
  );
}

function WithLabel() {
  const [value, setValue] = useState('intermediate');
  return (
    <Chips
      label="Difficulty"
      options={['beginner', 'intermediate', 'advanced']}
      value={value}
      onChange={setValue}
    />
  );
}

function WithDisabled() {
  const [value, setValue] = useState('react');
  return (
    <Chips
      options={[
        { value: 'react', label: 'react' },
        { value: 'vue', label: 'vue', disabled: true },
        { value: 'svelte', label: 'svelte' },
      ]}
      value={value}
      onChange={setValue}
    />
  );
}

export default function ChipsSnapshot() {
  return (
    <VariantGrid
      title="Chips"
      variants={[
        { label: 'single-select', node: <SingleSelect /> },
        { label: 'multi-select', node: <MultiSelect /> },
        { label: 'with-label', node: <WithLabel /> },
        { label: 'with-disabled', node: <WithDisabled /> },
      ]}
    />
  );
}

Props

Surface specific to <Chips />.

PropTypeDefaultDescription
multipleboolean
value*string | string[]
onChange*((value: string[]) => void) | ((value: string) => void)
options*readonly (string | ChipOption)[]Available options. Strings render as both display + value.
labelstringVisible label rendered above the chip group; also becomes the group's aria-labelledby target.
disabledbooleanDisable all chips.
classNamestringExtra Tailwind classes on the root group element.
aria-labelstringAccessible name when no visible label is provided.

Source