# Chip (/ui/components/react/chip)



<Playground component="chip" />

## Usage [#usage]

```tsx
import { Chip, ChipGroup } from '@appica/ui-react/chip'
```

```tsx
<Chip>Label</Chip>
```

`Chip` is a small, button-based pill for tags, attributes, filters, and removable tokens. It shares its look with [`Button`](/ui/components/react/button) (same `variant` palette) but comes in compact `sm` / `md` / `lg` heights. Make one removable with `dismissible` — it adds a close affordance and animates itself out when dismissed. Group several with `ChipGroup` to share `variant` / `size` and clear them all at once.

A `Chip` renders a native `<button>` by default. Swap the element with `render` (e.g. an `<a>` for a tag link), and reach for the shared `variant` palette to convey meaning.

## Examples [#examples]

### Variants [#variants]

`variant` sets the visual style, drawn from the same palette as [`Button`](/ui/components/react/button) — `soft` (default), `outline`, `primary`, `secondary`, and `destructive`.

```tsx
import { Chip } from '@appica/ui-react/chip'

const variants = ['soft', 'outline', 'primary', 'secondary', 'destructive'] as const

export default function ChipVariants() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      {variants.map((variant) => (
        <Chip key={variant} variant={variant} className="capitalize">
          {variant}
        </Chip>
      ))}
    </div>
  )
}
```

### Sizes [#sizes]

`size` scales the height, padding, text, and icon together — `sm`, `md` (default), or `lg`.

```tsx
import { Chip } from '@appica/ui-react/chip'

const sizes = [
  { size: 'sm', label: 'Small' },
  { size: 'md', label: 'Medium' },
  { size: 'lg', label: 'Large' },
] as const

export default function ChipSizes() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      {sizes.map(({ size, label }) => (
        <Chip key={size} size={size}>
          {label}
        </Chip>
      ))}
    </div>
  )
}
```

### With an icon [#with-an-icon]

Mark a leading or trailing [icon](/ui/icons) with `data-icon="start"` or `data-icon="end"` so the chip reserves the right inner spacing — the same convention as [`Button`](/ui/components/react/button).

```tsx
import { Chip } from '@appica/ui-react/chip'
import { Plus, CircleCheck, Star } from '@appica/icons-react'

export default function ChipWithIcon() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <Chip variant="soft">
        <Plus data-icon="start" />
        New
      </Chip>
      <Chip variant="primary">
        <CircleCheck data-icon="start" />
        Verified
      </Chip>
      <Chip variant="outline">
        4.9
        <Star data-icon="end" />
      </Chip>
    </div>
  )
}
```

### Dismissible [#dismissible]

Add `dismissible` to render a close (✕) affordance. Clicking the chip removes it with a blur-and-scale exit animation, then fires `onDismiss` once the animation finishes — the cue to drop it from your state.

```tsx
'use client'

import * as React from 'react'
import { Chip } from '@appica/ui-react/chip'

const initial = ['Design', 'Engineering', 'Marketing', 'Sales']

export default function ChipDismissible() {
  const [tags, setTags] = React.useState(initial)

  return (
    <div className="flex min-h-8 flex-wrap items-center gap-2">
      {tags.length === 0 ? (
        <button
          type="button"
          onClick={() => setTags(initial)}
          className="text-foreground-muted hover:text-foreground text-sm"
        >
          Restore tags
        </button>
      ) : (
        tags.map((tag) => (
          <Chip
            key={tag}
            dismissible
            closeLabel={`Remove ${tag}`}
            onDismiss={() => setTags((prev) => prev.filter((t) => t !== tag))}
          >
            {tag}
          </Chip>
        ))
      )}
    </div>
  )
}
```

### Selectable filters [#selectable-filters]

A chip is a button, so it's ready for selection UIs. Drive a `selected` state yourself and swap the `variant` (and a check icon) to show which is active; set `aria-pressed` so the toggle state is announced.

```tsx
'use client'

import * as React from 'react'
import { Chip } from '@appica/ui-react/chip'
import { Check } from '@appica/icons-react'

const options = ['All', 'Active', 'Completed', 'Archived']

export default function ChipSelectable() {
  const [selected, setSelected] = React.useState('Active')

  return (
    <div className="flex flex-wrap items-center gap-2">
      {options.map((option) => {
        const isSelected = option === selected
        return (
          <Chip
            key={option}
            variant={isSelected ? 'primary' : 'outline'}
            aria-pressed={isSelected}
            onClick={() => setSelected(option)}
          >
            {isSelected && <Check data-icon="start" />}
            {option}
          </Chip>
        )
      })}
    </div>
  )
}
```

### Chip group [#chip-group]

`ChipGroup` lays chips out in a wrapping row and shares `variant` / `size` with every child through context (a chip can still override its own). Capture a ref to call `clearAll()`, which dismisses every removable chip — each animates out and fires its `onDismiss`.

```tsx
'use client'

import * as React from 'react'
import { Chip, ChipGroup, type ChipGroupHandle } from '@appica/ui-react/chip'
import { Button } from '@appica/ui-react/button'

const filters = ['React', 'TypeScript', 'Tailwind', 'Motion', 'Base UI']

export default function ChipGroupDemo() {
  const groupRef = React.useRef<ChipGroupHandle>(null)
  const [active, setActive] = React.useState(filters)

  return (
    <div className="flex flex-col items-start gap-3">
      <ChipGroup ref={groupRef} variant="outline">
        {active.map((filter) => (
          <Chip key={filter} dismissible onDismiss={() => setActive((prev) => prev.filter((f) => f !== filter))}>
            {filter}
          </Chip>
        ))}
      </ChipGroup>
      <div className="flex gap-2">
        <Button size="sm" variant="ghost" onClick={() => groupRef.current?.clearAll()} disabled={active.length === 0}>
          Clear all
        </Button>
        <Button
          size="sm"
          variant="ghost"
          onClick={() => setActive(filters)}
          disabled={active.length === filters.length}
        >
          Reset
        </Button>
      </div>
    </div>
  )
}
```

### As a link [#as-a-link]

Because chips are often navigable tags, you can render one as an `<a>` (or your router's `Link`) with the `render` prop while keeping the chip styling — the `state` is still exposed to a render callback.

```tsx
import { Chip } from '@appica/ui-react/chip'

export default function ChipAsLink() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <Chip render={<a href="#design" />}>#design</Chip>
      <Chip render={<a href="#engineering" />}>#engineering</Chip>
      <Chip variant="primary" render={<a href="#featured" />}>
        #featured
      </Chip>
    </div>
  )
}
```

## RTL [#rtl]

Every Appica UI component supports right-to-left layouts out of the box. Set the `dir` attribute on a container (commonly your `<html>` element) so CSS logical properties resolve correctly, and wrap your tree in `DirectionProvider` so direction-aware behavior follows the same direction.

```tsx
import { DirectionProvider } from '@appica/ui-react/providers/direction-provider'

export default function RootLayout({ children }) {
  return (
    <html lang="ar" dir="rtl">
      <body>
        <DirectionProvider dir="rtl">{children}</DirectionProvider>
      </body>
    </html>
  )
}
```

The chip's content, inner icon spacing, and the dismiss button all use logical properties, so they mirror to the start/end edges automatically. For setup details and caveats, see the [RTL guide](/ui/docs/react/rtl).

<RtlPreview component="chip" />

## API reference [#api-reference]

### Chip [#chip]

A compact pill. Renders a native `<button>` by default.

| Prop           | <ColMinWidth width="200">Type</ColMinWidth>                                   | Default     | <ColMinWidth width="220">Description</ColMinWidth>                                                                                                                             |
| -------------- | ----------------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `variant`      | <Code>'soft' \| 'outline' \| 'primary' \| 'secondary' \| 'destructive'</Code> | `'soft'`    | Visual style, from the shared [`Button`](/ui/components/react/button) palette. Inherited from `ChipGroup`.                                                                     |
| `size`         | <Code>'sm' \| 'md' \| 'lg'</Code>                                             | `'md'`      | Height, padding, text, and icon size. Inherited from `ChipGroup`.                                                                                                              |
| `dismissible`  | `boolean`                                                                     | `false`     | Render a close button; clicking the chip dismisses it with an exit animation.                                                                                                  |
| `open`         | `boolean`                                                                     | —           | Controlled visibility for a **dismissible** chip — pair with `onOpenChange` to own dismissal in your own state. (For a non-dismissible chip, render it conditionally instead.) |
| `onOpenChange` | <Code>(open: boolean) => void</Code>                                          | —           | The **intent** signal — fires with `false` the moment a dismiss is requested. Use it with `open` for controlled mode: update your state here.                                  |
| `onDismiss`    | <Code>() => void</Code>                                                       | —           | The **completion** signal — fires once the exit animation finishes. Use it in uncontrolled mode to drop the chip from state after it's animated out.                           |
| `closeLabel`   | `string`                                                                      | `'Dismiss'` | Accessible label for the dismiss action (rendered as `sr-only` text).                                                                                                          |
| `disabled`     | `boolean`                                                                     | `false`     | Disables interaction; sets `data-disabled` for styling.                                                                                                                        |
| `render`       | <Code>ReactElement \| ((props, state) => ReactElement)</Code>                 | —           | Replace the underlying element (e.g. an `<a>`), or compose it with another component.                                                                                          |
| `className`    | `string`                                                                      | —           | Extra classes, merged after the variant classes via `tailwind-merge`.                                                                                                          |

Also forwards every remaining native `<button>` prop (`onClick`, `type`, `aria-*`, `ref`, …). The `render` callback receives the chip `state`: `{ variant, size, dismissible }`.

### ChipGroup [#chipgroup]

A wrapping flex container that shares defaults with its chips and can clear them as a set.

| Prop        | <ColMinWidth width="200">Type</ColMinWidth> | Default | <ColMinWidth width="220">Description</ColMinWidth>               |
| ----------- | ------------------------------------------- | ------- | ---------------------------------------------------------------- |
| `variant`   | <Code>ChipVariant</Code>                    | —       | Default `variant` for every child chip; a chip may override it.  |
| `size`      | <Code>'sm' \| 'md' \| 'lg'</Code>           | —       | Default `size` for every child chip; a chip may override it.     |
| `ref`       | <Code>Ref\<ChipGroupHandle></Code>          | —       | Exposes `clearAll()`, which dismisses every `dismissible` child. |
| `className` | `string`                                    | —       | Extra classes, merged via `tailwind-merge`.                      |

Renders a `<div>` and forwards every other prop (`id`, `aria-*`, …). `ChipGroupHandle` is `{ clearAll: () => void }`.

## Accessibility [#accessibility]

* A `Chip` is a native `<button>` (or whatever you pass to `render`), so it's focusable and in the tab order, and **Enter** / **Space** activate it.
* For a dismissible chip, the whole chip is the dismiss control; `closeLabel` provides an `sr-only` accessible name for the action — set it to something specific like `"Remove React"`.
* For selectable chips, set `aria-pressed` to expose the on/off state to assistive tech.
* `disabled` removes the chip from the tab order and sets `data-disabled` for styling.
* The dismiss exit animation is reduced to a simple fade when the user prefers reduced motion.
