# Editable Label Set

Editable Label Set [#editable-label-set]

The **Editable Label Set** is an editable collection of color-cycling removable chips for managing a candidate-label list. Chips reveal a remove control; an inline input adds new labels (Enter to commit, Backspace on empty to remove the last). It is fully controlled via `labels` / `onAdd` / `onRemove`, cycling a fixed color palette by index.

**When to use it:** editing the candidate labels you feed to `useClassifyZeroShot`, or any string-tag collection.

Preview [#preview]

```tsx
'use client';

import { useState } from 'react';
import { EditableLabelSet } from '@/components/editable-label-set';

/**
 * Demo for the EditableLabelSet component, used by the docs live preview.
 * Owns the controlled label list; add via the inline input, remove via the
 * chip's × control. Fully local.
 */
export default function EditableLabelSetDemo() {
  const [labels, setLabels] = useState([
    'technology',
    'business',
    'sports',
  ]);

  return (
    <div className="max-w-md">
      <EditableLabelSet
        labels={labels}
        onAdd={(label) => setLabels((prev) => [...prev, label])}
        onRemove={(_, index) =>
          setLabels((prev) => prev.filter((__, i) => i !== index))
        }
      />
    </div>
  );
}
```

Installation [#installation]

```bash
npx shadcn@latest add @localmode/ui/results/editable-label-set
```

Dependencies [#dependencies]

* **Data source:** fully controlled via the `labels` / `onAdd` / `onRemove` props you wire — works with any backend or local state. Recommended consumer: feed the labels to `useClassifyZeroShot` from `@localmode/react` (optional). See [Bring your own data](/docs/bring-your-own-data#results--insights).

* `lucide-react` — the remove (×) icon

* `clsx` + `tailwind-merge` — via the shared `cn()` util

Files installed [#files-installed]

* `editable-label-set.tsx` — the component
* `lib/utils.ts` — the `cn()` helper (if not already present)

Props [#props]

**EditableLabelSet**

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `labels` | `array` | — | **Required.** The controlled list of labels. |
| `onAdd` | `function` | — | **Required.** Invoked with the trimmed new label when the user adds one. |
| `onRemove` | `function` | — | **Required.** Invoked with the label (and its index) when the user removes a chip. |
| `placeholder` | `string` | `"Add a label…"` | Placeholder for the inline add input. |
| `palette` | `array` | — | Color palette cycled by chip index. Each entry is a CSS color (defaults are wired to CSS variables so chips theme via the consumer's tokens). |
| `maxLabels` | `number` | — | Disable adding when the count reaches this limit. |

Examples [#examples]

Feeding zero-shot candidate labels [#feeding-zero-shot-candidate-labels]

```tsx
import { useState } from 'react';
import { EditableLabelSet } from '@/components/editable-label-set';
import { useClassifyZeroShot } from '@localmode/react';

export function Example({ model }) {
  const [labels, setLabels] = useState(['positive', 'negative', 'neutral']);
  const { execute } = useClassifyZeroShot({ model });

  return (
    <EditableLabelSet
      labels={labels}
      onAdd={(l) => setLabels((p) => [...p, l])}
      onRemove={(_, i) => setLabels((p) => p.filter((__, j) => j !== i))}
    />
  );
}
```

Cap the number of labels [#cap-the-number-of-labels]

```tsx
<EditableLabelSet labels={labels} onAdd={add} onRemove={remove} maxLabels={6} />
```

Customization [#customization]

Chips use a CSS-variable color palette cycled by index — pass your own `palette` to match your design system. Each chip carries its hue on the dot, border, and a subtle background tint, while the &#x2A;*label text stays `text-foreground`** so it clears WCAG AA (4.5:1) in both light and dark themes regardless of the palette hue. The container and input use shadcn/ui tokens (`bg-card`, `border-border`, `text-foreground`). Because the set is fully controlled, dedup and validation live in your `onAdd` handler.