Chips
since v4.3.0Form 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/tungstenpnpm add @hey-mike/tungstenimport { 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 />.
| Prop | Type | Default | Description |
|---|---|---|---|
| multiple | boolean | — | — |
| value* | string | string[] | — | — |
| onChange* | ((value: string[]) => void) | ((value: string) => void) | — | — |
| options* | readonly (string | ChipOption)[] | — | Available options. Strings render as both display + value. |
| label | string | — | Visible label rendered above the chip group; also becomes the group's aria-labelledby target. |
| disabled | boolean | — | Disable all chips. |
| className | string | — | Extra Tailwind classes on the root group element. |
| aria-label | string | — | Accessible name when no visible label is provided. |