{"slug":"tags-input","title":"Tags Input","description":"Using the tags input machine in your project.","contentType":"component","framework":"react","content":"Tag inputs render tags inside an input, followed by an actual text input. By\ndefault, tags are added when text is typed in the input field and the `Enter` or\n`Comma` key is pressed. Throughout the interaction, DOM focus remains on the\ninput element.\n\n## Resources\n\n\n[Latest version: v1.31.0](https://www.npmjs.com/package/@zag-js/tags-input)\n[Logic Visualizer](https://zag-visualizer.vercel.app/tags-input)\n[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/tags-input)\n\n\n\n**Features**\n\n- Typing in the input and pressing enter will add new items\n- Clear button to reset all tags values\n- Add tags by pasting into the input\n- Delete tags on backspace\n- Edit tags after creation\n- Limit the number of tags\n- Navigate tags with keyboard\n- Custom validation to accept/reject tags\n\n## Installation\n\nTo use the tags input machine in your project, run the following command in your\ncommand line:\n\n```bash\nnpm install @zag-js/tags-input @zag-js/react\n# or\nyarn add @zag-js/tags-input @zag-js/react\n```\n\n## Anatomy\n\nTo set up the tags input correctly, you'll need to understand its anatomy and\nhow we name its parts.\n\n> Each part includes a `data-part` attribute to help identify them in the DOM.\n\n\n\n## Usage\n\nFirst, import the tags input package into your project\n\n```jsx\nimport * as tagsInput from \"@zag-js/tags-input\"\n```\n\nThe tags input package exports two key functions:\n\n- `machine` — The state machine logic for the tags input widget.\n- `connect` — The function that translates the machine's state to JSX attributes\n  and event handlers.\n\n> You'll also need to provide a unique `id` to the `useMachine` hook. This is\n> used to ensure that every part has a unique identifier.\n\nNext, import the required hooks and functions for your framework and use the\ntags input machine in your project 🔥\n\n```jsx\nimport * as tagsInput from \"@zag-js/tags-input\"\nimport { useMachine, normalizeProps } from \"@zag-js/react\"\n\nexport function TagsInput() {\n  const service = useMachine(tagsInput.machine, {\n    id: \"1\",\n    value: [\"React\", \"Vue\"],\n  })\n\n  const api = tagsInput.connect(service, normalizeProps)\n\n  return (\n    <div {...api.getRootProps()}>\n      {api.value.map((value, index) => (\n        <span key={index} {...api.getItemProps({ index, value })}>\n          <div {...api.getItemPreviewProps({ index, value })}>\n            <span>{value} </span>\n            <button {...api.getItemDeleteTriggerProps({ index, value })}>\n              &#x2715;\n            </button>\n          </div>\n          <input {...api.getItemInputProps({ index, value })} />\n        </span>\n      ))}\n      <input placeholder=\"Add tag...\" {...api.getInputProps()} />\n    </div>\n  )\n}\n```\n\n### Navigating and Editing tags\n\nWhen the input has an empty value or the caret is at the start position, the\ntags can be selected by using the arrow left and arrow right keys. When \"visual\"\nfocus in on any tag:\n\n- Pressing `Enter` or double clicking on the tag will put the it in edit mode,\n  allowing the user change its value and press `Enter` to commit the changes.\n- Pressing `Delete` or `Backspace` will delete the tag that has \"visual\" focus.\n\n### Setting the initial tags\n\nTo set the initial tag values, pass the `defaultValue` property in the machine's\ncontext.\n\n```jsx {2}\nconst service = useMachine(tagsInput.machine, {\n  defaultValue: [\"React\", \"Redux\", \"TypeScript\"],\n})\n```\n\n### Controlled tags input\n\nTo control the tags input programmatically, pass the `value` and `onValueChange`\nproperties to the machine function.\n\n```tsx\nimport { useState } from \"react\"\n\nexport function ControlledTagsInput() {\n  const [value, setValue] = useState([\"React\", \"Vue\"])\n\n  const service = useMachine(tagsInput.machine, {\n    value,\n    onValueChange(details) {\n      setValue(details.value)\n    },\n  })\n\n  return ( // ... )\n}\n```\n\n### Removing all tags\n\nThe tags input will remove all tags when the clear button is clicked. To remove\nall tags, use the provided `clearButtonProps` function from the `api`.\n\n```jsx {4}\n//...\n<div {...api.getControlProps()}>\n  <input {...api.getInputProps()} />\n  <button {...api.getClearButtonProps()} />\n</div>\n//...\n```\n\nTo programmatically remove all tags, use the `api.clearAll()` method that's\navailable in the `connect`.\n\n### Usage within forms\n\nThe tags input works when placed within a form and the form is submitted. We\nachieve this by:\n\n- ensuring we emit the input event as the value changes.\n- adding a `name` and `value` attribute to a hidden input so the tags can be\n  accessed in the `FormData`.\n\nTo get this feature working you need to pass a `name` option to the context and\nrender the `hiddenInput` element.\n\n```jsx {2}\nconst service = useMachine(tagsInput.machine, {\n  name: \"tags\",\n  defaultValue: [\"React\", \"Redux\", \"TypeScript\"],\n})\n```\n\n### Limiting the number of tags\n\nTo limit the number of tags within the component, you can set the `max` property\nto the limit you want. The default value is `Infinity`.\n\nWhen the tag reaches the limit, new tags cannot be added except the\n`allowOverflow` option is passed to the context.\n\n```jsx {2-3}\nconst service = useMachine(tagsInput.machine, {\n  max: 10,\n  allowOverflow: true,\n})\n```\n\n### Validating Tags\n\nBefore a tag is added, the machine provides a `validate` function you can use to\ndetermine whether to accept or reject a tag.\n\nA common use-case for validating tags is preventing duplicates or validating the\ndata type.\n\n```jsx {2-3}\nconst service = useMachine(tagsInput.machine, {\n  validate(details) {\n    return !details.values.includes(details.inputValue)\n  },\n})\n```\n\n### Blur behavior\n\nWhen the tags input is blurred, you can configure the action the machine should\ntake by passing the `blurBehavior` option to the context.\n\n- `\"add\"` — Adds the tag to the list of tags.\n- `\"clear\"` — Clears the tags input value.\n\n```jsx {2}\nconst service = useMachine(tagsInput.machine, {\n  blurBehavior: \"add\",\n})\n```\n\n### Paste behavior\n\nTo add a tag when a arbitrary value is pasted in the input element, pass the\n`addOnPaste` option.\n\nWhen a value is pasted, the machine will:\n\n- check if the value is a valid tag based on the `validate` option\n- split the value by the `delimiter` option passed\n\n```jsx {2}\nconst service = useMachine(tagsInput.machine, {\n  addOnPaste: true,\n})\n```\n\n### Disable tag editing\n\nby default the tags can be edited by double clicking on the tag or focusing on\nthem and pressing `Enter`. To disable this behavior, pass the\n`allowEditTag: false`\n\n```jsx {2}\nconst service = useMachine(tagsInput.machine, {\n  allowEditTag: false,\n})\n```\n\n### Listening for events\n\nDuring the lifetime of the tags input interaction, here's a list of events we\nemit:\n\n- `onValueChange` — invoked when the tag value changes.\n- `onHighlightChange` — invoked when a tag has visual focus.\n- `onValueInvalid` — invoked when the max tag count is reached or the `validate`\n  function returns `false`.\n\n```jsx\nconst service = useMachine(tagsInput.machine, {\n  onValueChange(details) {\n    // details => { value: string[] }\n    console.log(\"tags changed to:\", details.value)\n  },\n  onHighlightChange(details) {\n    // details => { value: string }\n    console.log(\"highlighted tag:\", details.value)\n  },\n  onValueInvalid(details) {\n    console.log(\"Invalid!\", details.reason)\n  },\n})\n```\n\n## Styling guide\n\nEarlier, we mentioned that each accordion part has a `data-part` attribute added\nto them to select and style them in the DOM.\n\n### Focused state\n\nThe combobox input is focused when the user clicks on the input element. In this\nfocused state, the root, label, input.\n\n```css\n[data-part=\"root\"][data-focus] {\n  /* styles for root focus state */\n}\n\n[data-part=\"label\"][data-focus] {\n  /* styles for label focus state */\n}\n\n[data-part=\"input\"]:focus {\n  /* styles for input focus state */\n}\n```\n\n### Invalid state\n\nWhen the tags input is invalid by setting the `invalid: true` in the machine's\ncontext, the `data-invalid` attribute is set on the root, input, control, and\nlabel.\n\n```css\n[data-part=\"root\"][data-invalid] {\n  /* styles for invalid state for root */\n}\n\n[data-part=\"label\"][data-invalid] {\n  /*  styles for invalid state for label */\n}\n\n[data-part=\"input\"][data-invalid] {\n  /*  styles for invalid state for input */\n}\n```\n\n### Disabled state\n\nWhen the tags input is disabled by setting the `disabled: true` in the machine's\ncontext, the `data-disabled` attribute is set on the root, input, control and\nlabel.\n\n```css\n[data-part=\"root\"][data-disabled] {\n  /* styles for disabled state for root */\n}\n\n[data-part=\"label\"][data-disabled] {\n  /* styles for disabled state for label */\n}\n\n[data-part=\"input\"][data-disabled] {\n  /* styles for disabled state for input */\n}\n\n[data-part=\"control\"][data-disabled] {\n  /* styles for disabled state for control */\n}\n```\n\nWhen a tag is disabled, the `data-disabled` attribute is set on the tag.\n\n```css\n[data-part=\"item-preview\"][data-disabled] {\n  /* styles for disabled tag  */\n}\n```\n\n### Highlighted state\n\nWhen a tag is highlighted via the keyboard navigation or pointer hover, a\n`data-highlighted` attribute is set on the tag.\n\n```css\n[data-part=\"item-preview\"][data-highlighted] {\n  /* styles for visual focus */\n}\n```\n\n### Readonly state\n\nWhen the tags input is in its readonly state, the `data-readonly` attribute is\nset on the root, label, input and control.\n\n```css\n[data-part=\"root\"][data-readonly] {\n  /* styles for readonly for root */\n}\n\n[data-part=\"control\"][data-readonly] {\n  /* styles for readonly for control */\n}\n\n[data-part=\"input\"][data-readonly] {\n  /* styles for readonly for input  */\n}\n\n[data-part=\"label\"][data-readonly] {\n  /* styles for readonly for label */\n}\n```\n\n## Methods and Properties\n\n### Machine Context\n\nThe tags input machine exposes the following context properties:\n\n**`ids`**\nType: `Partial<{ root: string; input: string; hiddenInput: string; clearBtn: string; label: string; control: string; item: (opts: ItemProps) => string; itemDeleteTrigger: (opts: ItemProps) => string; itemInput: (opts: ItemProps) => string; }>`\nDescription: The ids of the elements in the tags input. Useful for composition.\n\n**`translations`**\nType: `IntlTranslations`\nDescription: Specifies the localized strings that identifies the accessibility elements and their states\n\n**`maxLength`**\nType: `number`\nDescription: The max length of the input.\n\n**`delimiter`**\nType: `string | RegExp`\nDescription: The character that serves has:\n- event key to trigger the addition of a new tag\n- character used to split tags when pasting into the input\n\n**`autoFocus`**\nType: `boolean`\nDescription: Whether the input should be auto-focused\n\n**`disabled`**\nType: `boolean`\nDescription: Whether the tags input should be disabled\n\n**`readOnly`**\nType: `boolean`\nDescription: Whether the tags input should be read-only\n\n**`invalid`**\nType: `boolean`\nDescription: Whether the tags input is invalid\n\n**`required`**\nType: `boolean`\nDescription: Whether the tags input is required\n\n**`editable`**\nType: `boolean`\nDescription: Whether a tag can be edited after creation, by pressing `Enter` or double clicking.\n\n**`inputValue`**\nType: `string`\nDescription: The controlled tag input's value\n\n**`defaultInputValue`**\nType: `string`\nDescription: The initial tag input value when rendered.\nUse when you don't need to control the tag input value.\n\n**`value`**\nType: `string[]`\nDescription: The controlled tag value\n\n**`defaultValue`**\nType: `string[]`\nDescription: The initial tag value when rendered.\nUse when you don't need to control the tag value.\n\n**`onValueChange`**\nType: `(details: ValueChangeDetails) => void`\nDescription: Callback fired when the tag values is updated\n\n**`onInputValueChange`**\nType: `(details: InputValueChangeDetails) => void`\nDescription: Callback fired when the input value is updated\n\n**`onHighlightChange`**\nType: `(details: HighlightChangeDetails) => void`\nDescription: Callback fired when a tag is highlighted by pointer or keyboard navigation\n\n**`onValueInvalid`**\nType: `(details: ValidityChangeDetails) => void`\nDescription: Callback fired when the max tag count is reached or the `validateTag` function returns `false`\n\n**`validate`**\nType: `(details: ValidateArgs) => boolean`\nDescription: Returns a boolean that determines whether a tag can be added.\nUseful for preventing duplicates or invalid tag values.\n\n**`blurBehavior`**\nType: `\"clear\" | \"add\"`\nDescription: The behavior of the tags input when the input is blurred\n- `\"add\"`: add the input value as a new tag\n- `\"clear\"`: clear the input value\n\n**`addOnPaste`**\nType: `boolean`\nDescription: Whether to add a tag when you paste values into the tag input\n\n**`max`**\nType: `number`\nDescription: The max number of tags\n\n**`allowOverflow`**\nType: `boolean`\nDescription: Whether to allow tags to exceed max. In this case,\nwe'll attach `data-invalid` to the root\n\n**`name`**\nType: `string`\nDescription: The name attribute for the input. Useful for form submissions\n\n**`form`**\nType: `string`\nDescription: The associate form of the underlying input element.\n\n**`dir`**\nType: `\"ltr\" | \"rtl\"`\nDescription: The document's text/writing direction.\n\n**`id`**\nType: `string`\nDescription: The unique identifier of the machine.\n\n**`getRootNode`**\nType: `() => ShadowRoot | Node | Document`\nDescription: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.\n\n**`onPointerDownOutside`**\nType: `(event: PointerDownOutsideEvent) => void`\nDescription: Function called when the pointer is pressed down outside the component\n\n**`onFocusOutside`**\nType: `(event: FocusOutsideEvent) => void`\nDescription: Function called when the focus is moved outside the component\n\n**`onInteractOutside`**\nType: `(event: InteractOutsideEvent) => void`\nDescription: Function called when an interaction happens outside the component\n\n### Machine API\n\nThe tags input `api` exposes the following methods:\n\n**`empty`**\nType: `boolean`\nDescription: Whether the tags are empty\n\n**`inputValue`**\nType: `string`\nDescription: The value of the tags entry input.\n\n**`value`**\nType: `string[]`\nDescription: The value of the tags as an array of strings.\n\n**`valueAsString`**\nType: `string`\nDescription: The value of the tags as a string.\n\n**`count`**\nType: `number`\nDescription: The number of the tags.\n\n**`atMax`**\nType: `boolean`\nDescription: Whether the tags have reached the max limit.\n\n**`setValue`**\nType: `(value: string[]) => void`\nDescription: Function to set the value of the tags.\n\n**`clearValue`**\nType: `(id?: string) => void`\nDescription: Function to clear the value of the tags.\n\n**`addValue`**\nType: `(value: string) => void`\nDescription: Function to add a tag to the tags.\n\n**`setValueAtIndex`**\nType: `(index: number, value: string) => void`\nDescription: Function to set the value of a tag at the given index.\n\n**`setInputValue`**\nType: `(value: string) => void`\nDescription: Function to set the value of the tags entry input.\n\n**`clearInputValue`**\nType: `VoidFunction`\nDescription: Function to clear the value of the tags entry input.\n\n**`focus`**\nType: `VoidFunction`\nDescription: Function to focus the tags entry input.\n\n**`getItemState`**\nType: `(props: ItemProps) => ItemState`\nDescription: Returns the state of a tag\n\n### Data Attributes\n\n**`Root`**\n\n**`data-scope`**: tags-input\n**`data-part`**: root\n**`data-invalid`**: Present when invalid\n**`data-readonly`**: Present when read-only\n**`data-disabled`**: Present when disabled\n**`data-focus`**: Present when focused\n**`data-empty`**: Present when the content is empty\n\n**`Label`**\n\n**`data-scope`**: tags-input\n**`data-part`**: label\n**`data-disabled`**: Present when disabled\n**`data-invalid`**: Present when invalid\n**`data-readonly`**: Present when read-only\n**`data-required`**: Present when required\n\n**`Control`**\n\n**`data-scope`**: tags-input\n**`data-part`**: control\n**`data-disabled`**: Present when disabled\n**`data-readonly`**: Present when read-only\n**`data-invalid`**: Present when invalid\n**`data-focus`**: Present when focused\n\n**`Input`**\n\n**`data-scope`**: tags-input\n**`data-part`**: input\n**`data-invalid`**: Present when invalid\n**`data-readonly`**: Present when read-only\n**`data-empty`**: Present when the content is empty\n\n**`Item`**\n\n**`data-scope`**: tags-input\n**`data-part`**: item\n**`data-value`**: The value of the item\n**`data-disabled`**: Present when disabled\n\n**`ItemPreview`**\n\n**`data-scope`**: tags-input\n**`data-part`**: item-preview\n**`data-value`**: The value of the item\n**`data-disabled`**: Present when disabled\n**`data-highlighted`**: Present when highlighted\n\n**`ItemText`**\n\n**`data-scope`**: tags-input\n**`data-part`**: item-text\n**`data-disabled`**: Present when disabled\n**`data-highlighted`**: Present when highlighted\n\n**`ItemDeleteTrigger`**\n\n**`data-scope`**: tags-input\n**`data-part`**: item-delete-trigger\n**`data-disabled`**: Present when disabled\n**`data-highlighted`**: Present when highlighted\n\n**`ClearTrigger`**\n\n**`data-scope`**: tags-input\n**`data-part`**: clear-trigger\n**`data-readonly`**: Present when read-only\n\n## Accessibility\n\n### Keyboard Interactions\n\n**`ArrowLeft`**\nDescription: Moves focus to the previous tag item\n\n**`ArrowRight`**\nDescription: Moves focus to the next tag item\n\n**`Backspace`**\nDescription: Deletes the tag item that has visual focus or the last tag item\n\n**`Enter`**\nDescription: <span>When a tag item has visual focus, it puts the tag in edit mode.<br />When the input has focus, it adds the value to the list of tags</span>\n\n**`Delete`**\nDescription: Deletes the tag item that has visual focus\n\n**`Control + V`**\nDescription: When `addOnPaste` is set. Adds the pasted value as a tags","package":"@zag-js/tags-input","editUrl":"https://github.com/chakra-ui/zag/edit/main/website/data/components/tags-input.mdx"}