# Chip

> Filter, choice and input chips: selecting one fills it with the accent while a check draws itself in, removing one shrinks it away, and a ChipGroup in single mode moves one elastic highlight from chip to chip.

- Type: Component (inputs)
- Access: Free, open source (MIT)
- Page: https://goodui.dev/native/components/chip
- Markdown: https://goodui.dev/native/components/chip.md
- Registry item: https://goodui.dev/native/r/chip.json
- Dependencies: react-native-reanimated
- Keywords: react native chip, filter chips, choice chips, tag input, removable tag, pill selector, multi select chips, expo chips

## When to use

- Filters above a list or map: cuisine, price, distance, open now.
- Picking one or several short options in a form: time slots, sizes, interests.
- Showing entered values that can be removed: recipients, tags, applied filters.
- Suggestions the person can tap to act on (action chips with onPress).

## When not to use

- Switching between two to five views of the same content: use segmented-control.
- Read-only status: use badge.
- A long list of options or anything that needs search: use a picker or a bottom-sheet list.
- The main action of a screen: use button.

## Installation

With the shadcn CLI (needs a components.json in the Expo app), then `npx expo install --fix` so native dependencies match your Expo SDK. The foundation comes along the first time.

```bash
npx shadcn@latest add https://goodui.dev/native/r/chip.json
npx expo install --fix
```

The component lands in `components/goodui-native/chip/` and imports the foundation from `components/goodui-native/foundation/` with relative paths; it also installs locale.

Built-in strings follow `<GooduiLocaleProvider>` (one [language pack](https://goodui.dev/native/r/locale-tr.json) translates the whole kit); the `labels` prop overrides them for one instance.

## Usage

```tsx
import { useState } from "react";
import { ChipGroup } from "@/components/goodui-native/chip/chip";

type Slot = "09:00" | "10:30" | "13:00" | "15:30";

export function TimeSlots() {
  const [slot, setSlot] = useState<Slot[]>(["10:30"]);
  return (
    <ChipGroup<Slot>
      accessibilityLabel="Appointment time"
      value={slot}
      onValueChange={setSlot}
      options={[
        { value: "09:00", label: "9:00" },
        { value: "10:30", label: "10:30" },
        { value: "13:00", label: "1:00 PM" },
        { value: "15:30", label: "3:30 PM", disabled: true },
      ]}
    />
  );
}
```

## Examples

### Multiple filters in a scrolling row, at most three

```tsx
<ChipGroup
  mode="multiple"
  layout="scroll"
  max={3}
  defaultValue={["open"]}
  accessibilityLabel="Filters"
  contentContainerStyle={{ paddingHorizontal: 16 }}
  options={[
    { value: "open", label: "Open now" },
    { value: "near", label: "Under 2 km" },
    { value: "cheap", label: "€" },
    { value: "vegan", label: "Vegan" },
    { value: "rated", label: "4.5+" },
  ]}
/>
```

### Removable input chips

```tsx
{recipients.map((r) => (
  <Chip key={r.id} label={r.name} onRemove={() => setRecipients((all) => all.filter((x) => x.id !== r.id))} />
))}
```

### A single toggle chip

```tsx
<Chip label="Only refundable" selected={refundable} onSelectedChange={setRefundable} />
```

## API reference

### Chip

`import { Chip } from "@/components/goodui-native/chip/chip";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` (required) | `string` | – | Short text, sentence case. |
| `selected` | `boolean` | – | Controlled selected state. Passing it (or `defaultSelected` or `onSelectedChange`) makes the chip a toggle. |
| `defaultSelected` | `boolean` | – | Uncontrolled starting state. |
| `onSelectedChange` | `(selected: boolean) => void` | – | Called with the new state when the chip is toggled. |
| `onPress` | `() => void` | – | For action chips that do not toggle, such as a suggestion. |
| `onRemove` | `() => void` | – | Shows an × button. The chip shrinks away first, then this runs, so the list can drop it without a jump. |
| `leading` | `ChipLeading` | – | Icon or avatar before the label, 16 to 18 pt. When selected, the check takes its place. |
| `variant` | `"soft" \| "outline"` | `"outline"` | `outline` has a hairline border, `soft` a quiet fill. Selected chips fill with the accent. |
| `size` | `"sm" \| "md"` | `"md"` | 32 or 36 pt tall, with a 44 pt touch target. |
| `check` | `boolean` | `true` | Draw a check in when selected. Default `true`. |
| `disabled` | `boolean` | `false` | Dims the control and ignores presses. |
| `haptic` | `HapticName \| false` | `"select"` | Haptic on toggle or press. `false` turns it off. |
| `accessibilityLabel` | `string` | – | Overrides the label read by screen readers. |
| `labels` | `Partial<ChipLabels>` | – | Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. |
| `style` | `StyleProp<ViewStyle>` | – | Style for the outer container. |
| `testID` | `string` | – | Identifier for end-to-end tests. |

### ChipGroup

`import { ChipGroup } from "@/components/goodui-native/chip/chip";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `options` (required) | `ChipOption<T>[]` | – | The chips, in order. |
| `mode` | `"single" \| "multiple"` | `"single"` | `single` behaves like radio buttons with one highlight that springs between chips; `multiple` like checkboxes. |
| `value` | `T[]` | – | Controlled selection. Always an array; in `single` mode it holds at most one value. |
| `defaultValue` | `T[]` | – | Uncontrolled starting selection. |
| `onValueChange` | `(value: T[]) => void` | – | Called with the new selection. |
| `allowEmpty` | `boolean` | `false` | In `single` mode, tapping the selected chip clears the selection. Default `false`. |
| `max` | `number` | – | In `multiple` mode, the most chips that can be selected. Past it the tap is refused with a warning haptic. |
| `layout` | `"wrap" \| "scroll"` | `"wrap"` | `wrap` flows onto new lines; `scroll` is one horizontal row that keeps the selected chip in view. |
| `variant` | `"soft" \| "outline"` | `"outline"` | – |
| `size` | `"sm" \| "md"` | `"md"` | – |
| `haptic` | `HapticName \| false` | `"select"` | Haptic when the selection changes. |
| `accessibilityLabel` | `string` | – | Names the group for screen readers, for example "Cuisine". |
| `labels` | `Partial<ChipLabels>` | – | Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. |
| `style` | `StyleProp<ViewStyle>` | – | Style for the outer container. |
| `contentContainerStyle` | `StyleProp<ViewStyle>` | – | Padding for the scrolling row, usually the screen gutter. |
| `testID` | `string` | – | Identifier for end-to-end tests. |

## Accessibility

- A toggle Chip is a checkbox; in a ChipGroup chips are radios (single mode, inside a radiogroup) or checkboxes (multiple), with checked and disabled in accessibilityState.
- Action chips are buttons; chips with neither are read as text.
- The × is its own button labelled 'Remove <label>', and removal is announced.
- Hitting the max in multiple mode is refused with a warning haptic and announced ('Choose up to 3').
- Chips are 32 or 36 pt tall with a vertical hitSlop up to 44 pt.

## Motion

- Selecting fills the chip and recolors the label with a timing; the check slot opens on the snappy spring and the check draws itself in. With a leading icon, the check crossfades in its place.
- Single-mode ChipGroup: one accent highlight travels between chips. The edge in the direction of travel leads on the snappy spring and the trailing edge follows on smooth, so it stretches and settles; top and height follow on morph when it changes lines.
- Press scales the chip to 0.95 on the snappy spring from touch down.
- Remove: the chip's real width closes while it fades and scales to 0.8, then onRemove runs, so neighbors slide over.
- In the scroll layout the row scrolls the newly selected chip into view.
- Reduced motion: the highlight jumps, the check appears without drawing, removal is a fade, no press scale.

## Haptics

- select when a chip toggles or the group selection changes (configurable with haptic)
- warning when a selection is refused at max
- tap on remove

## Performance

- Colors, the highlight and removal run on the UI thread. Chip layouts are measured once per real change; the highlight only moves when the selection or the selected chip's box changes.

## Notes for AI

- ChipGroup value is always an array, also in single mode: value={[slot]} onValueChange={([v]) => setSlot(v)}. Single mode cannot be cleared unless allowEmpty is set.
- A Chip becomes a toggle when you pass selected, defaultSelected or onSelectedChange; otherwise pass onPress for an action chip or nothing for a static one.
- Keep a removed chip mounted until onRemove runs; then drop it from your list.
- Use layout='scroll' with contentContainerStyle={{ paddingHorizontal: 16 }} for a full-bleed filter row.
- Labels are short and sentence case. Put icons in leading (a function receives the label color).

## Related

- [Segmented control](https://goodui.dev/native/components/segmented-control.md): Two to five views of the same thing with an elastic highlight that stretches toward the new segment, can be dragged, and ticks at every boundary. Includes a billing toggle.
- [Badge](https://goodui.dev/native/components/badge.md): A count, dot or status badge that springs in when it appears, pops when its value changes, caps at 99+ and shrinks away at zero; BadgeAnchor pins it to an icon or avatar.
- [Bottom sheet](https://goodui.dev/native/components/bottom-sheet.md): A sheet that rests at detents, follows the finger 1:1, settles with velocity and hands drags off to its scroll view.

## Guidance for AI tools

Chip: Filter, choice and input chips: selecting one fills it with the accent while a check draws itself in, removing one shrinks it away, and a ChipGroup in single mode moves one elastic highlight from chip to chip. Use only the documented props. Keep accessibility, reduced motion and both color schemes intact when adapting it.

Full library index: https://goodui.dev/native/llms.txt
