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
    data-slot="tags-input"
    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
    data-slot="tags-input-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
    data-slot="tags-input-list"
    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
    data-slot="tags-input-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
    data-slot="tags-input-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 = React.forwardRef<
  React.ComponentRef<typeof TagsInputPrimitive.Clear>,
  React.ComponentPropsWithoutRef<typeof TagsInputPrimitive.Clear>
>(({ className, ...props }, ref) => (
  <TagsInputPrimitive.Clear data-slot="tags-input-clear" ref={ref} {...props} />
));
TagsInputClear.displayName = TagsInputPrimitive.Clear.displayName;
 
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