Skip to content

Tags Input

Alpha
Tag inputs render tags inside an input, followed by an actual text input.
Apple
Banana
vue
<script setup lang="ts">
import { ref } from 'vue'
import { TagsInputInput, TagsInputItem, TagsInputItemDelete, TagsInputItemText, TagsInputRoot } from 'radix-vue'
import { Icon } from '@iconify/vue'

const modelValue = ref(['Apple', 'Banana'])
</script>

<template>
  <TagsInputRoot
    v-model="modelValue"
    class="flex gap-2 items-center border p-2 rounded-lg w-full max-w-[480px] flex-wrap border-blackA7 bg-white"
  >
    <TagsInputItem v-for="item in modelValue" :key="item" :value="item" class="text-white flex shadow-md items-center justify-center gap-2 bg-green8 aria-[current=true]:bg-green9 rounded p-1">
      <TagsInputItemText class="text-sm pl-1" />
      <TagsInputItemDelete class="p-0.5 rounded bg-transparent hover:bg-blackA4">
        <Icon icon="lucide:x" />
      </TagsInputItemDelete>
    </TagsInputItem>

    <TagsInputInput placeholder="Fruits..." class="text-sm focus:outline-none flex-1 rounded text-green9 bg-transparent placeholder:text-mauve9 px-1" />
  </TagsInputRoot>
</template>

Features

  • Can be controlled or uncontrolled.
  • Full keyboard navigation.
  • Limit the number of tags.
  • Accept value from clipboard.
  • Clear button to reset all tags values.

Installation

Install the component from your command line.

bash
npm install radix-vue

Anatomy

Import all parts and piece them together.

vue
<script setup>
import { TagsInputClear, TagsInputDelete, TagsInputInput, TagsInputItem, TagsInputRoot, TagsInputText } from 'radix-vue'
</script>

<template>
  <TagsInputRoot>
    <TagsInputItem>
      <TagsInputItemText />
      <TagsInputItemDelete />
    </TagsInputItem>

    <TagsInputInput />
    <TagsInputClear />
  </TagsInputRoot>
</template>

API Reference

Root

Contains all the tags input component parts.

PropDefaultType
addOnPaste
boolean

When true, allow adding tags on paste. Work in conjunction with delimiter prop.

as
'div'
AsTag | Component

The element or component this component should render as. Can be overwrite by asChild

asChild
boolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

defaultValue
[]
string[]

The value of the tags that should be added. Use when you do not need to control the state of the tags input

delimiter
','
string

The character to trigger the addition of a new tag. Also used to split tags for @paste event

dir
'ltr' | 'rtl'

The reading direction of the combobox when applicable.
If omitted, inherits globally from DirectionProvider or assumes LTR (left-to-right) reading mode.

disabled
boolean

When true, prevents the user from interacting with the tags input.

duplicate
boolean

When true, allow duplicated tags.

id
string
max
0
number

Maximum number of tags.

modelValue
string[]

The controlled value of the tags input. Can be bind as v-model.

name
string

The name of the tags input submitted with its owning form as part of a name/value pair.

required
boolean

When true, indicates that the user must add the tags input before the owning form can be submitted.

EmitPayload
invalid
[payload: string]

Event handler called when the value is invalid

update:modelValue
[payload: string[]]

Event handler called when the value changes

Slots (default)Payload
modelValue
string[]

Current input values

Data AttributeValue
[data-disabled]Present when disabled
[data-focused]Present when focus on input
[data-invalid]Present when input value is invalid

Item

The component that contains the tag.

PropDefaultType
as
'div'
AsTag | Component

The element or component this component should render as. Can be overwrite by asChild

asChild
boolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

disabled
boolean

When true, prevents the user from interacting with the tags input.

value*
string

Value associated with the tags

Data AttributeValue
[data-state]"active" | "inactive"
[data-disabled]Present when disabled

ItemText

The textual part of the tag. Important for accessibility.

PropDefaultType
as
'span'
AsTag | Component

The element or component this component should render as. Can be overwrite by asChild

asChild
boolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

ItemDelete

The button that delete the associate tag.

PropDefaultType
as
'button'
AsTag | Component

The element or component this component should render as. Can be overwrite by asChild

asChild
boolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

Data AttributeValue
[data-state]"active" | "inactive"
[data-disabled]Present when disabled

Input

The input element for the tags input.

PropDefaultType
as
'input'
AsTag | Component

The element or component this component should render as. Can be overwrite by asChild

asChild
boolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

autoFocus
boolean

Focus on element when mounted.

maxLength
number

Maximum number of character allowed.

placeholder
string

The placeholder character to use for empty tags input.

Data AttributeValue
[data-invalid]Present when input value is invalid

Clear

The button that remove all tags.

PropDefaultType
as
'button'
AsTag | Component

The element or component this component should render as. Can be overwrite by asChild

asChild
boolean

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

Data AttributeValue
[data-disabled]Present when disabled

Examples

With Combobox

You can compose Tags input together with Combobox.

Apple
vue
<script setup lang="ts">
import { ref, watch } from 'vue'
import { ComboboxAnchor, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxLabel, ComboboxRoot, ComboboxTrigger, ComboboxViewport, TagsInputInput, TagsInputItem, TagsInputItemDelete, TagsInputItemText, TagsInputRoot } from 'radix-vue'
import { Icon } from '@iconify/vue'

const searchTerm = ref('')
const values = ref(['Apple'])
const options = ['Apple', 'Banana', 'Blueberry', 'Grapes', 'Pineapple']

watch(values, () => {
  searchTerm.value = ''
}, { deep: true })
</script>

<template>
  <ComboboxRoot
    v-model="values"
    v-model:search-term="searchTerm"
    multiple
    class="my-4 mx-auto relative"
  >
    <ComboboxAnchor class="w-[400px] inline-flex items-center justify-between rounded-lg p-2 text-[13px] leading-none  gap-[5px] bg-white text-grass11 shadow-[0_2px_10px] shadow-black/10 hover:bg-mauve3 focus:shadow-[0_0_0_2px] focus:shadow-black data-[placeholder]:text-grass9 outline-none">
      <TagsInputRoot
        v-slot="{ modelValue: tags }"
        :model-value="values"
        delimiter=""
        class="flex gap-2 items-center rounded-lg flex-wrap"
      >
        <TagsInputItem
          v-for="item in tags" :key="item"
          :value="item"
          class="flex items-center justify-center gap-2 text-white bg-grass8 aria-[current=true]:bg-grass9 rounded px-2 py-1"
        >
          <TagsInputItemText class="text-sm" />
          <TagsInputItemDelete>
            <Icon icon="lucide:x" />
          </TagsInputItemDelete>
        </TagsInputItem>

        <ComboboxInput as-child>
          <TagsInputInput
            placeholder="Fruits..."
            class="focus:outline-none flex-1 rounded !bg-transparent  placeholder:text-mauve10 px-1"
            @keydown.enter.prevent
          />
        </ComboboxInput>
      </TagsInputRoot>

      <ComboboxTrigger>
        <Icon icon="radix-icons:chevron-down" class="h-4 w-4 text-grass11" />
      </ComboboxTrigger>
    </ComboboxAnchor>
    <ComboboxContent class="absolute z-10 w-full mt-2 bg-white overflow-hidden rounded shadow-[0px_10px_38px_-10px_rgba(22,_23,_24,_0.35),_0px_10px_20px_-15px_rgba(22,_23,_24,_0.2)] will-change-[opacity,transform] data-[side=top]:animate-slideDownAndFade data-[side=right]:animate-slideLeftAndFade data-[side=bottom]:animate-slideUpAndFade data-[side=left]:animate-slideRightAndFade">
      <ComboboxViewport class="p-[5px]">
        <ComboboxEmpty class="text-gray-400  text-xs font-medium text-center py-2" />

        <ComboboxGroup>
          <ComboboxLabel class="px-[25px] text-xs leading-[25px] text-mauve11">
            Fruits
          </ComboboxLabel>

          <ComboboxItem
            v-for="(option, index) in options" :key="index"
            class="text-[13px] leading-none text-grass11 rounded-[3px] flex items-center h-[25px] pr-[35px] pl-[25px] relative select-none data-[disabled]:text-mauve8 data-[disabled]:pointer-events-none data-[highlighted]:outline-none data-[highlighted]:bg-grass8 data-[highlighted]:text-grass1"
            :value="option"
          >
            <ComboboxItemIndicator
              class="absolute left-0 w-[25px] inline-flex items-center justify-center"
            >
              <Icon icon="radix-icons:check" />
            </ComboboxItemIndicator>
            <span>
              {{ option }}
            </span>
          </ComboboxItem>
        </ComboboxGroup>
      </ComboboxViewport>
    </ComboboxContent>
  </ComboboxRoot>
</template>

Paste behavior

You can automatically add tags on paste by passing the add-on-paste prop.

vue
<script setup lang="ts">
import { TagsInputInput, TagsInputItem, TagsInputItemDelete, TagsInputItemText, TagsInputRoot } from 'radix-vue'
</script>

<template>
  <TagsInputRoot v-model="modelValue" add-on-paste>

  </TagsInputRoot>
</template>

Accessibility

Keyboard Interactions

KeyDescription
Delete
When tag is active, remove it and set the tag on right active.
Backspace
When tag is active, remove it and set the tag on left active. If there are no tags to the left, either the next tags gets focus, or the input.
ArrowRight
Set the next tag active.
ArrowLeft
Set the previous tag active.
Home
Set the first tag active
End
Set the last tag active