

<Preview name="DefaultTagInputDemo" />

## Overview [#overview]

`TagInput` lets users add, remove, and select multiple tags. It supports predefined options, free-typed tags via Enter/comma, CSV/TSV/newline pasting, and backspace-to-remove on an empty input.

## Usage [#usage]

```tsx
import { TagInput } from "@tilt-legal/cubitt-components/primitives";

const options = [
  { id: "react", label: "React" },
  { id: "ts", label: "TypeScript" },
  { id: "ui", label: "UI/UX" },
];

export default function Component() {
  return <TagInput options={options} placeholder="e.g. TypeScript" />;
}
```

<Accordions type="single">
  <Accordion title="URL State Management">
    Sync tags with the URL by providing `paramName`. The component serializes to a string array automatically.

    ```tsx
    // Synced with ?demo-tags=foo,bar (when tags are added)
    <TagInput paramName="demo-tags" placeholder="Add tags..." />

    // Advanced options
    <TagInput
      paramName="labels"
      paramClearOnDefault
      paramDebounce={300}
      onUrlValueChange={(value) => console.log("tags:", value)}
    />
    ```
  </Accordion>
</Accordions>

## Examples [#examples]

### Default (predefined options) [#default-predefined-options]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="DefaultTagInputDemo" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { TagInput } from "@tilt-legal/cubitt-components/primitives";

    const options = [
      { id: "react", label: "React" },
      { id: "ts", label: "TypeScript" },
      { id: "ui", label: "UI/UX" },
    ];

    export default function Component() {
      return <TagInput options={options} placeholder="Add tags" />;
    }
    ```
  </Tab>
</Tabs>

### Free entry + remove last on backspace [#free-entry--remove-last-on-backspace]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="FreeEntryTagInputDemo" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { useState } from "react";
    import { TagInput } from "@tilt-legal/cubitt-components/primitives";

    export default function Component() {
      const [tags, setTags] = useState([]);
      return (
        <TagInput
          tags={tags}
          onTagsChange={setTags}
          placeholder="Type and press Enter or ,"
        />
      );
    }
    ```
  </Tab>
</Tabs>

### Paste CSV/TSV/newlines [#paste-csvtsvnewlines]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="PasteTagInputDemo" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { TagInput } from "@tilt-legal/cubitt-components/primitives";

    export default function Component() {
      return (
        <TagInput
          enableExcelPaste
          pasteDelimiters={[",", "\n", "\t"]}
          placeholder="Paste a CSV/TSV list"
        />
      );
    }
    ```
  </Tab>
</Tabs>

### Expanded field [#expanded-field]

Increase the field height when tags are likely to wrap across several rows or when you want a larger area to click and start typing.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="ExpandedTagInputDemo" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { useState } from "react";
    import { TagInput } from "@tilt-legal/cubitt-components/primitives";

    const initialTags = [
      { id: "contract-review", label: "Contract Review" },
      { id: "employment", label: "Employment" },
      { id: "urgent", label: "Urgent" },
    ];

    export default function Component() {
      const [tags, setTags] = useState(initialTags);

      return (
        <TagInput
          className="w-full max-w-xs [&_[data-slot=combobox-chips]]:min-h-28 [&_[data-slot=combobox-chips]]:content-start [&_[data-slot=combobox-chips]]:items-start"
          onTagsChange={setTags}
          placeholder="Add tags"
          tags={tags}
        />
      );
    }
    ```
  </Tab>
</Tabs>

### URL State [#url-state]

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="URLStateTagInputDemo" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import { TagInput } from "@tilt-legal/cubitt-components/primitives";

    // Synced with ?demo-tags=foo,bar
    export default function Component() {
      return <TagInput paramName="demo-tags" placeholder="Synced with ?demo-tags=" />;
    }
    ```
  </Tab>
</Tabs>

## API Reference [#api-reference]

### TagInput [#taginput]

| Prop                   | Type                                    | Default                     | Description                                   |
| ---------------------- | --------------------------------------- | --------------------------- | --------------------------------------------- |
| `tags`                 | `{ id: string; label: string }[]`       | —                           | Controlled tags.                              |
| `defaultTags`          | `{ id: string; label: string }[]`       | —                           | Uncontrolled initial tags.                    |
| `onTagsChange`         | `(next: Tag[]) => void`                 | —                           | Change handler for controlled usage.          |
| `options`              | `{ id: string; label: string }[]`       | —                           | Predefined selectable options.                |
| `variant`              | `"sm" \| "md" \| "lg"`                  | `"md"`                      | Size variant.                                 |
| `placeholder`          | `string`                                | —                           | Placeholder for the input.                    |
| `className`            | `string`                                | —                           | Additional className for the wrapper.         |
| `inputProps`           | `InputHTMLAttributes<HTMLInputElement>` | —                           | Forward id/name/aria/blur to the inner input. |
| `allowDuplicates`      | `boolean`                               | `false`                     | Allow duplicate tag labels.                   |
| `enableExcelPaste`     | `boolean`                               | `true`                      | Split pasted CSV/TSV/newlines into tags.      |
| `enableCommaDelimiter` | `boolean`                               | `true`                      | Comma key creates a tag.                      |
| `pasteDelimiters`      | `string[]`                              | `[",", "\n", "\r\n", "\t"]` | Delimiters used when splitting pasted text.   |

#### URL State Props [#url-state-props]

When `paramName` is provided, `TagInput` syncs its tags to a string\[] URL parameter via TanStack Router search params.

| Prop                  | Type                              | Default | Description                                               |
| --------------------- | --------------------------------- | ------- | --------------------------------------------------------- |
| `paramName`           | `string`                          | —       | URL parameter name to sync. Enables URL state management. |
| `onUrlValueChange`    | `(value: string[]\|null) => void` | —       | Callback when URL parameter value changes.                |
| `paramClearOnDefault` | `boolean`                         | `true`  | Remove URL param when value equals default.               |
| `paramThrottle`       | `number`                          | —       | Throttle URL updates in milliseconds.                     |
| `paramDebounce`       | `number`                          | —       | Debounce URL updates in milliseconds.                     |
