Phi
Tag Input
@dicehub/phiv1.0.0-beta.1

Tag Input

A creatable multi-value input for values that do not come from a predefined option list.

<script setup>
import { ref } from "vue";
import { TagInput } from "@dicehub/phi/components/tag-input";

const recipients = ref(["[email protected]"]);
</script>

<template>
  <TagInput
    v-model="recipients"
    label="Recipients"
    description="Paste comma- or newline-separated email addresses."
    placeholder="[email protected]"
    :validate-value="(value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)"
  />
</template>

Installation

Barrel

import { TagInput } from "@dicehub/phi";

Granular

import { TagInput } from "@dicehub/phi/components/tag-input";

Usage

Use v-model for controlled tags and defaultValue for uncontrolled usage. Enter, comma, Tab, and blur commit the current text.

<script setup>
import { ref } from "vue";
import { TagInput } from "@dicehub/phi/components/tag-input";

const labels = ref(["frontend"]);
</script>

<template>
  <TagInput v-model="labels" label="Labels" placeholder="Add a label" />
</template>

The component exposes its native input as input, so a template ref can focus the field or read the underlying element.

Examples

Unrestricted Values

Uncontrolled usage seeds the tags with defaultValue and accepts any non-empty value.

<template>
  <TagInput
    label="Labels"
    description="Accepts any non-empty value."
    name="labels"
    placeholder="Add a label"
    required
    :default-value="['frontend', 'priority']"
  />
</template>

Maximum Values

maxValues stops further tags, keeps the rejected text in the input, and shows the localized limit message.

<template>
  <TagInput
    label="Access groups"
    placeholder="Type a group name"
    description="A maximum of three groups."
    :max-values="3"
    :default-value="['alpha']"
  />
</template>

Validation

validateValue receives the candidate tag and the already accepted tags. Rejected text stays in the input with the invalid message.

<script setup>
import { ref } from "vue";
import { TagInput } from "@dicehub/phi/components/tag-input";

const environments = ref(["staging"]);
</script>

<template>
  <TagInput
    v-model="environments"
    label="Environments"
    description="Lowercase names only."
    placeholder="Add an environment"
    :max-values="4"
    :validate-value="(value) => value === value.toLowerCase()"
  />
</template>

Localization

labels replaces every string TagInput generates: the fallback input name, remove actions, and validation feedback.

<script setup>
import { TagInput } from "@dicehub/phi/components/tag-input";

const labels = {
  input: "Agregar etiqueta",
  removeValue: (value) => `Eliminar ${value}`,
  invalidValue: (value) => `${value} no es valido.`,
  maxValuesReached: (maxValues) => `Maximo de ${maxValues} etiquetas.`,
};
</script>

<template>
  <TagInput label="Etiquetas" placeholder="Agregar una etiqueta" :labels="labels" :max-values="3" />
</template>

Fallback Accessible Name

Without a label or aria-label, the input keeps the labels.input name, so the control stays named. Override labels.input to localize it.

<template>
  <TagInput placeholder="Add a tag" :default-value="['draft']" />
  <TagInput
    placeholder="Agregar una etiqueta"
    :labels="{ input: 'Agregar etiqueta' }"
  />
</template>

Disabled

Disabled TagInput blocks typing and tag removal.

<template>
  <TagInput
    label="Regions"
    description="Locked while the deployment is running."
    disabled
    :default-value="['eu-central', 'us-east']"
  />
</template>

Bare TagInput (Custom Layouts)

Without label, description, or error, TagInput renders the control only. Provide aria-label or aria-labelledby.

<template>
  <TagInput aria-label="Notes" placeholder="Add a note" :default-value="['checked']" />
</template>

Sizes

TagInput uses the Input size scale for text, radius, and base height, and reserves extra room for tags at the smaller sizes.

<template>
  <TagInput size="xs" label="Extra small" placeholder="xs" :default-value="['tag']" />
  <TagInput size="sm" label="Small" placeholder="sm" :default-value="['tag']" />
  <TagInput label="Base" placeholder="base (default)" :default-value="['tag']" />
  <TagInput size="lg" label="Large" placeholder="lg" :default-value="['tag']" />
</template>

Behavior

  • Enter, comma, or Tab commits the current text as a tag. Blur commits the text too.
  • Pasting comma- or newline-separated text creates every value synchronously.
  • Duplicate values are ignored, so each tag appears once.
  • Backspace removes the last tag only while the input is empty.
  • When maxValues is reached or validateValue rejects a value, the remaining text stays in the input and the matching localized message replaces the description.

API Reference

TagInput accepts standard HTML input attributes plus the following:

Props

PropTypeDefaultDescription
modelValuestring[]undefinedControlled tags. Use with `v-model` or `update:modelValue`.
defaultValuestring[][]Initial tags for uncontrolled usage.
labelstringundefinedVisible label. Enables the built-in field wrapper.
labelTooltipstringundefinedTooltip text shown from the label.
descriptionstringundefinedHelper text shown under the control when there is no error.
errorstring | { message: string; match?: InputErrorMatch }undefinedError message that marks the control invalid.
invalidbooleanfalseMarks the control invalid. The field keeps the error slot in place of the description.
requiredbooleanundefinedSets the required state. `false` renders the optional hint.
disabledbooleanfalseBlocks typing and tag removal.
size"xs" | "sm" | "base" | "lg""base"Control height and text size.
variant"default" | "error""default"Visual variant. Errors and invalid state resolve to `error`.
placeholderstringundefinedPlaceholder for the tag input.
namestringundefinedForm field name. Each accepted tag is submitted with this name.
idstringgeneratedId for the native input that the label points at.
autoCompletestring"off"Autocomplete hint for the native input.
maxValuesnumberundefinedMaximum number of tags. Further values are rejected.
validateValue(value: string, acceptedValues: string[]) => booleanundefinedRejects a candidate tag before it is added.
labelsTagInputLabelsundefinedOverrides the generated input, remove, invalid, and limit strings.

Slots

SlotDescription
labelReplaces the label content.
descriptionReplaces the description content.
errorReplaces the error content.

Events

EventTypeDescription
update:modelValue(value: string[]) => voidEmitted whenever tags are added or removed.
valueChange(value: string[]) => voidSame payload as `update:modelValue`, for non-`v-model` listeners.

Labels

Use labels to translate every string TagInput generates.

KeyTypeDescription
inputstringAccessible name when there is no visible label or aria-label.
removeValue(value: string) => stringAccessible name of a tag remove button.
invalidValue(value: string) => stringFeedback when validateValue rejects a value.
maxValuesReached(maxValues: number) => stringFeedback when maxValues is reached.

Accessibility

Label Requirement

Prefer the label prop or label slot. Without a visible label, TagInput falls back toaria-label and then to the localized labels.input name.

Tag Removal

Every tag renders a button named by labels.removeValue, so assistive technology can remove a tag without keyboard navigation through the input.

Validation Feedback

Invalid and over-limit states set aria-invalid and associate the message througharia-describedby.