Dice UI
Components

Tags Input

Display a list of tags in an input field with the ability to add, edit, and remove them.

Installation

npm install @diceui/tags-input

Installation with shadcn/ui

CLI

npx shadcn@latest add "https://diceui.com/r/tags-input"

Manual

Install the following dependencies:

npm install @diceui/tags-input

Copy and paste the following code into your project.

import { cn } from "@/lib/utils";
import * as TagsInputPrimitive from "@diceui/tags-input";
import { X } from "lucide-react";
import * as React from "react";
 
const TagsInput = React.forwardRef<
  React.ComponentRef<typeof TagsInputPrimitive.Root>,
  React.ComponentPropsWithoutRef<typeof TagsInputPrimitive.Root>
>(({ className, ...props }, ref) => (
  <TagsInputPrimitive.Root
    ref={ref}
    className={cn("flex w-[380px] flex-col gap-2", className)}
    {...props}
  />
));
TagsInput.displayName = TagsInputPrimitive.Root.displayName;
 
const TagsInputLabel = React.forwardRef<
  React.ComponentRef<typeof TagsInputPrimitive.Label>,
  React.ComponentPropsWithoutRef<typeof TagsInputPrimitive.Label>
>(({ className, ...props }, ref) => (
  <TagsInputPrimitive.Label
    ref={ref}
    className={cn(
      "font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
      className,
    )}
    {...props}
  />
));
TagsInputLabel.displayName = TagsInputPrimitive.Label.displayName;
 
const TagsInputList = React.forwardRef<
  HTMLDivElement,
  React.ComponentPropsWithoutRef<"div">
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    className={cn(
      "flex min-h-10 w-full flex-wrap items-center gap-1.5 rounded-md border border-input bg-background px-3 py-2 text-sm focus-within:ring-1 focus-within:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
      className,
    )}
    {...props}
  />
));
TagsInputList.displayName = "TagsInputList";
 
const TagsInputInput = React.forwardRef<
  React.ComponentRef<typeof TagsInputPrimitive.Input>,
  React.ComponentPropsWithoutRef<typeof TagsInputPrimitive.Input>
>(({ className, ...props }, ref) => (
  <TagsInputPrimitive.Input
    ref={ref}
    className={cn(
      "flex-1 bg-transparent outline-hidden placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
      className,
    )}
    {...props}
  />
));
TagsInputInput.displayName = TagsInputPrimitive.Input.displayName;
 
const TagsInputItem = React.forwardRef<
  React.ComponentRef<typeof TagsInputPrimitive.Item>,
  React.ComponentPropsWithoutRef<typeof TagsInputPrimitive.Item>
>(({ className, children, ...props }, ref) => (
  <TagsInputPrimitive.Item
    ref={ref}
    className={cn(
      "inline-flex max-w-[calc(100%-8px)] items-center gap-1.5 rounded border bg-transparent px-2.5 py-1 text-sm focus:outline-hidden data-disabled:cursor-not-allowed data-editable:select-none data-editing:bg-transparent data-disabled:opacity-50 data-editing:ring-1 data-editing:ring-ring [&:not([data-editing])]:pr-1.5 [&[data-highlighted]:not([data-editing])]:bg-accent [&[data-highlighted]:not([data-editing])]:text-accent-foreground",
      className,
    )}
    {...props}
  >
    <TagsInputPrimitive.ItemText className="truncate">
      {children}
    </TagsInputPrimitive.ItemText>
    <TagsInputPrimitive.ItemDelete className="h-4 w-4 shrink-0 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100">
      <X className="h-3.5 w-3.5" />
    </TagsInputPrimitive.ItemDelete>
  </TagsInputPrimitive.Item>
));
TagsInputItem.displayName = TagsInputPrimitive.Item.displayName;
 
const TagsInputClear = TagsInputPrimitive.Clear;
 
export {
  TagsInput,
  TagsInputLabel,
  TagsInputList,
  TagsInputInput,
  TagsInputItem,
  TagsInputClear,
};

Layout

Import the parts, and compose them together.

import * as TagsInput from "@diceui/tags-input";
 
<TagsInput.Root>
  <TagsInput.Label/>
  <TagsInput.Item >
    <TagsInput.ItemText />
    <TagsInput.ItemDelete />
  </TagsInput.Item>
  <TagsInput.Input />
  <TagsInput.Clear />
</TagsInput.Root>

Examples

Editable

Kickflip
Heelflip
FS 540

With Validation

Validate the input value before adding it to the list. Can be used to prevent duplicate tags.

Add up to 6 tricks with at least 3 characters, excluding "ollie".

With Sortable

TagsInput can be composed with Sortable to allow reordering of tags.

The 900
FS 540

API Reference

Root

Container for the tags input.

PropTypeDefault
defaultValue
string[]
-
value
string[]
-
onValueChange
(value: string[]) => void
-
asChild
boolean
-
onValidate
(value: string) => boolean
-
displayValue
(value: string) => string
-
addOnPaste
boolean
false
addOnTab
boolean
false
disabled
boolean
false
editable
boolean
false
loop
boolean
false
blurBehavior
"add" | "clear"
-
delimiter
string
","
max
number
Number.POSITIVE_INFINITY
required
boolean
false
readOnly
boolean
false
name
string
-
Data AttributeValue
[data-disabled]Present when disabled.
[data-invalid]Present when invalid.
[data-readonly]Present when readOnly.

Label

Label element for the tags input.

PropTypeDefault
asChild
boolean
-
Data AttributeValue
[data-disabled]Present when the tags input is disabled.

Input

Text input for adding new tags.

PropTypeDefault
asChild
boolean
-
Data AttributeValue
[data-invalid]Present when the input value is invalid.

Item

Individual tag item.

PropTypeDefault
asChild
boolean
-
value
string
-
disabled
boolean
-
Data AttributeValue
[data-state]"active" | "inactive"
[data-highlighted]Present when the tag is highlighted.
[data-disabled]Present when the tag is disabled.
[data-editing]Present when the tag is being edited.
[data-editable]Present when the tags input is editable.

ItemText

Text content of a tag.

PropTypeDefault
asChild
boolean
-

ItemDelete

Button to remove a tag.

PropTypeDefault
asChild
boolean
-
Data AttributeValue
[data-disabled]Present when the delete button is disabled.
[data-state]"active" | "inactive"

Clear

Button to clear all tags.

PropTypeDefault
asChild
boolean
-
forceMount
boolean
false
Data AttributeValue
[data-disabled]Present when the clear button is disabled.
[data-state]"visible" | "invisible"

Accessibility

Keyboard Interactions

KeyDescription
DeleteWhen a tag is highlighted, removes it and sets focus to the next tag.
BackspaceWhen a tag is highlighted, removes it and sets focus to the previous tag. If there is no previous tag, focus moves to the next tag or input.
ArrowLeftHighlights the previous tag. When cursor is at start of input, moves focus to the last tag.
ArrowRightHighlights the next tag. When cursor is at start of input, moves focus to the first tag.
HomeHighlights the first tag in the list.
EndHighlights the last tag in the list.
EnterWhen input has text, adds a new tag. When a tag is highlighted and editable is enabled, enters edit mode.
EscapeClears tag highlight and edit mode, resets cursor to start.
TabWhen `addOnTab` is enabled and input has text, adds a new tag. Otherwise, blurs the input.

On this page