Skip to content

Captures and displays tag-like token values.

import { BbTag } from 'bitboss-ui';

On this page

Props

NameDescription
append:icon Type: string

Name of the icon to be added at the end of the input.

autocomplete Type: stringDefault"off"

Guides to the browser as to the type of information expected in the field.

autofocus Type: Booleanish

Focuses the field when it mounts (a dialog already open included), unless the user is typing in another field.

caseSensitive Type: booleanDefaultfalse

Compares tags case-sensitively when rejecting duplicates (Vue and vue both allowed); by default case folds. A tag keeps the casing typed either way, and entries are trimmed first.

Restores v2 matching. By default v3 folds case, so Vue typed after vue emits duplicate instead of adding a second chip. Either way the tag is stored with the casing that was typed — only the comparison folds.

See example
clearable Type: booleanDefaultfalse

Displays a clear button when the input has a value and is being interacted with.

Empties the whole field, not one chip. In comma mode it is the only remaining button, since that mode renders no per-chip close.

See example
comma Type: booleanDefaultfalse

Sets the display model for selected values to a list of comma separated string. In this mode the user cannot deselect an option by pressing the close button.

A display mode: the tags render as one comma-separated string and lose their individual close buttons. Use it where the set is set-and-forget, never where people prune a list.

See example
compact Type: booleanDefaultfalse

Compact density: the field uses the -compact twins of --bb-input-h, --bb-input-py and --bb-label-spacing-y, so a --bb-input-h set on the component is ignored while it is on. Size a compact field with --bb-input-h-compact (and --bb-input-py-compact).

A property of a screen rather than of a field. Two densities in one form is a visible seam.

See example
description Type: string

Text between the label and the input. Unlike the hint it is always visible; it joins the input's aria-describedby after any errors / warnings, before the hint.

disabled Type: booleanDefaultfalse

Disables the component.

Leaves the tab order entirely, chips included. Prefer readonly for an inherited set someone may need to read or copy.

See example
divider Type: string | string[]Default"Enter"

The divider key used to separate values from one another. By default it is "Enter". Accepts a single key or an array of keys (e.g. ['Enter', ',']) — any of them commits the current text, split on every single-character divider in the set into one tag per trimmed, non-empty piece.

A single-character divider also splits text that already contains it, so a pasted bug, regression, docs becomes three chips. The cost is exact: a comma divider makes a comma impossible to type inside a tag. Do not add keys speculatively.

See example
errors Type: string | string[]

Error messages under the field, a string or array, in an aria-live="polite" region and first in the control's aria-describedby, so they are re-read when it regains focus.

The channel for a rule the component does not enforce — "at least three tags", a value your server rejected. A refused duplicate or a capped entry is better placed in warnings: nothing is wrong with the field.

See example
formless Type: booleanDefaultfalse

Keep this control out of the enclosing BbForm entirely: not submitted, not dirty, not reset, not validated. For a control that is not form data (a filter, a "show advanced" switch).

For a tags field inside a BbForm that is not form data, like an inherited, read-only set.

See example
hasErrors Type: booleanDefaultfalse

Define if the component should be in an error state. It usually attaches a CSS class for styling purposes.

hasWarnings Type: booleanDefaultfalse

Define if the component should be in a warning state. Same chrome as errors with --bb-warn; errors take priority.

hideLabel Type: booleanDefaultfalse

Visually hides the label of the input while maintaining accessibility.

hint Type: string

Text box to be displayed near the input, usually to indicate instructions.

id Type: string

The id of the native control; the label's for and the description and message ids derive from it. Generated when omitted.

inputAlign Type: FieldCrossAlign

Vertical position of the control in a side-by-side row; not inputPosition, the horizontal one. Omitted, the control's first line meets the label's.

label requiredType: string

Text content of the label of the element.

labelAlign Type: FieldCrossAlign

Vertical position of the label block in a side-by-side row; not labelPosition, the horizontal one. Omitted, the label's first line meets the control's.

labelMode Type: "outside" | "floating" | "inside"

Where the label sits relative to the field: outside above it, inside within the control, or floating (starts inside, lifts on focus/value). Defaults to config.defaultInputLabelMode, like the other inputs.

All three modes work here, unlike on BbSlider. Unset, the value comes from defaultInputLabelMode in the plugin config, so a tags field matches the rest of a form without being configured.

See example
labelPosition Type: "left" | "center" | "right"

Sets the text alignment of the label.

layout Type: Responsive<FieldLayout>

How label and input sit together: 'vertical', 'horizontal' (50/50), 'auto', 'label-fill', 'input-fill', a ratio like 'xx xxxxx', or a per-breakpoint map ({ default: 'vertical', md: 'xx xxxxx' }).

Applies only while the resolved label mode is outside. floating and inside embed the label in the field and force the vertical layout, ignoring this silently.

See example
loading Type: booleanDefaultfalse

Sets the component in a loading state, usually triggering some visual styles.

A status light, not a lock: the field stays editable while it shows. Guard the value yourself if a server-side check has to settle first.

See example
max Type: number

Maximum number of selected items.

Checked against undefined, so :max="0" is a real quota of zero rather than "unlimited". Past the cap the entry is refused and the max event carries it — do not mutate the array from that handler to make room.

See example
modelValue Type: string[]

Used by v-model. Contains all selected tags.

name Type: string

Defines the name of the input.

persistentHint Type: booleanDefaultfalse

Keeps the hint displayed.

placeholder Type: string

Text shown while the field is empty.

prepend:icon Type: string

Name of the icon to be added at the start of the input.

pt Type: (PtMap<TextControlPtPart, FormControlPtState> & { "badge:loading"?: PtValue | undefined; "badge:disabled"?: PtValue | undefined; "badge:errors"?: PtValue | undefined; "badge:warnings"?: PtValue | undefined; "badge:readonly"?: PtValue | undefined; "badge:hasValue"?: PtValue | undefined; "comma:loading"?: PtValue | undefined; "comma:disabled"?: PtValue | undefined; "comma:errors"?: PtValue | undefined; "comma:warnings"?: PtValue | undefined; "comma:readonly"?: PtValue | undefined; "comma:hasValue"?: PtValue | undefined; "badge:highlighted"?: PtValue | undefined; "comma:highlighted"?: PtValue | undefined; } & { badge?: PtValue | PtItemFn<string, BbTagPtState> | undefined; comma?: PtValue | PtItemFn<string, BbTagPtState> | undefined; } & { "root:outline"?: PtValue | undefined; "icon:outline"?: PtValue | undefined; "root:secondary"?: PtValue | undefined; "icon:secondary"?: PtValue | undefined; "spinner:outline"?: PtValue | undefined; "spinner:secondary"?: PtValue | undefined; "clear:outline"?: PtValue | undefined; "clear:secondary"?: PtValue | undefined; "root:ghost"?: PtValue | undefined; "icon:ghost"?: PtValue | undefined; "spinner:ghost"?: PtValue | undefined; "clear:ghost"?: PtValue | undefined; "label:outline"?: PtValue | undefined; "label:secondary"?: PtValue | undefined; "label:ghost"?: PtValue | undefined; "description:outline"?: PtValue | undefined; "description:secondary"?: PtValue | undefined; "description:ghost"?: PtValue | undefined; "message:outline"?: PtValue | undefined; "message:secondary"?: PtValue | undefined; "message:ghost"?: PtValue | undefined; "hint:outline"?: PtValue | undefined; "hint:secondary"?: PtValue | undefined; "hint:ghost"?: PtValue | undefined; "input:outline"?: PtValue | undefined; "input:secondary"?: PtValue | undefined; "input:ghost"?: PtValue | undefined; "box:outline"?: PtValue | undefined; "box:secondary"?: PtValue | undefined; "box:ghost"?: PtValue | undefined; "prefix:outline"?: PtValue | undefined; "prefix:secondary"?: PtValue | undefined; "prefix:ghost"?: PtValue | undefined; "suffix:outline"?: PtValue | undefined; "suffix:secondary"?: PtValue | undefined; "suffix:ghost"?: PtValue | undefined; })

Passthrough, object form: the same keys as the pt:* attributes without the prefix. See the pt:<part> row.

pt:<part> Type: PtValue | ((ctx: PtItemCtx<string, BbTagPtState>) => PtItemResult)

Passthrough, attribute form: one attribute per part, pt:<part>, and per part and state, pt:<part>:<state>. Parts: badge, box, clear, comma, description, hint, icon, input, label, message, prefix, root, spinner, suffix. States: disabled, errors, ghost, hasValue, highlighted, loading, outline, readonly, secondary, warnings.

Each chip is pt:badge and each entry in comma mode is pt:comma (they replace pt:item). Both also take a function called once per tag, ({ item, highlighted }) => …, where item is the tag string.

See example
readonly Type: booleanDefaultfalse

Sets the input in a readonly state.

Locks this input field. It is not a way to render tags elsewhere in the interface — a table cell or a card wants BbBadge, which is the same chip without the field chrome.

See example
required Type: booleanDefaultfalse

Sets the input as required.

Satisfied by the first tag: the underlying input drops its required attribute as soon as the array is non-empty. Any other threshold is a function in rules inside a BbForm, or a message you compute and pass through errors outside one.

See example
reverse Type: booleanDefaultfalse

Reverses the layout. Applicable in every direction the order of the label and the input is swapped.

rules Type: RuleExpression<string[]>

vee-validate rules for this field. Only inside a BbForm.

Validates the whole array as one value, so a function over it covers a minimum or a maximum: (tags) => (tags?.length ?? 0) >= 3 || 'Add at least three keywords.'.

See example
validationLabel Type: string

The field's name in validation messages ({field}): "Email is not valid". Defaults to the label (or legend), then "This field"; never the name. Set it when the label is hidden or reads badly in a sentence (icon-only, a question). BbForm only.

validationMode Type: ValidationMode

When the field validates: aggressive (each change), balanced (on leave, then live while in error), lazy (on leave), submit-only, submit-live. Unset: the BbForm's validation-mode, then formControls.validationMode ('balanced'). BbForm only.

variant Type: InputVariantTypeDefault'outline'

The field box's look, with BbButton's names and tokens. Colours only, so a form never reflows; 'ghost' has no border in any state, 'none' no look at all (you own hover, focus and the states). Register more with the plugin's inputVariants.

The field box only: outline (default), secondary and ghost keep the same height, padding and border width, so swapping one never shifts a layout.

See example
warnings Type: string | string[]

Warning messages under the field, a string or array: amber, in their own aria-live region and in the control's aria-describedby after the errors. Non-empty turns on the warning state; errors win when both are set.

v-model

Values kept in sync through v-model.

NameDescription
modelValue Type: string[]

Strictly string[] in v3. Outside a BbForm it must already be an array on the first render: a scalar or an undefined model throws at mount. Inside a form you can leave v-model off and the form starts the field at []. A model of objects no longer compiles: keep the objects in your own state and bind a mapped string[]. If the strings come from a list you already have, the component you want is BbSelect in multiple mode.

See example

Events

NameDescription
active Type: (): void

Emitted when the tag input becomes active (focus/click enters it).

blur Type: (event: FocusEvent): void

Emitted when the native input loses focus. Forwards the original DOM FocusEvent.

change Type: (event: Event): void

Emitted when the native input fires a change event. Forwards the original DOM Event.

click Type: (event: MouseEvent): void

Emitted when the input receives a click interaction. Forwards the original DOM MouseEvent.

compositionend Type: (event: CompositionEvent): void

An IME composition ended. Forwards the DOM CompositionEvent.

compositionstart Type: (event: CompositionEvent): void

An IME composition started. Forwards the DOM CompositionEvent.

duplicate Type: (string: string): void

Emitted when an attempt is made to add a tag that already exists.

Carries the rejected string. Nothing is shown to the user unless you show it — the component refuses the entry and says nothing on screen.

See example
focus Type: (event: FocusEvent): void

Emitted when the native input gains focus. Forwards the original DOM FocusEvent.

inactive Type: (): void

Emitted when focus/click moves outside the tag input after it was active.

input Type: (event: Event): void

Emitted on native input events. Forwards the original DOM Event.

keydown Type: (event: KeyboardEvent): void

Emitted for keyboard interaction. Forwards the original DOM KeyboardEvent.

keyup Type: (event: KeyboardEvent): void

Emitted when a key is released. Forwards the original DOM KeyboardEvent.

max Type: (string: string): void

Emitted when an attempt is made to add a tag while the max limit is already reached.

The cap counterpart of duplicate, and it carries the rejected string for the same reason: so the message can name it.

See example
mousedown Type: (event: MouseEvent): void

A pointer button was pressed on the text field. Forwards the DOM MouseEvent.

mouseup Type: (event: MouseEvent): void

A pointer button was released on the text field. Forwards the DOM MouseEvent.

paste Type: (event: ClipboardEvent): void

Native clipboard paste. Forwards the DOM ClipboardEvent.

update:modelValue Type: (value: string[]): void

Emitted when the tag array changes.

Slots

The listed properties are the ones exposed to the slot scope.

NameDescription
append Type: Record<string, never>

Content appended inside the input field wrapper, after the tag chips.

append:outer Type: Record<string, never>

Content appended outside the input field wrapper.

description Type: BbTagDescriptionSlotProps

Replaces the description text. Provided alone (no description prop) it still renders the description region and wires it into aria-describedby.

label Type: BbTagLabelSlotProps

Replaces the default label text rendered above the input.

prefix Type: Record<string, never>

Content rendered as an inline prefix inside the input field.

prepend Type: Record<string, never>

Content prepended inside the input field wrapper, before the tag chips.

prepend:outer Type: Record<string, never>

Content prepended outside the input field wrapper.

suffix Type: Record<string, never>

Content rendered as an inline suffix inside the input field.

Renamed and removed

Names from v2 and from earlier 3.0 betas that no longer work, and what to write instead.

  • slotappend-outerappend:outerfails silently
  • directionlayoutfails silently
  • hasWarninghasWarningsfails silently
  • multipleremovedfails silently
  • slotprepend-outerprepend:outerfails silently
  • pt:itemremovedfails silently

    Each chip is pt:badge; in comma mode each entry is pt:comma (same states)

  • validateOnvalidationModefails silently

    An array of events became one mode (aggressive | balanced | lazy | submit-only | submit-live). The old defaults (['blur'], ['inactive'], a toggle's ['update:modelValue', 'change', 'blur']) are balanced, the new default: delete the prop. A text field on ['update:modelValue'] is validation-mode="aggressive". Set it once on <BbForm validation-mode> instead of per field.

  • vidremovedfails silently

    A display name (v2: vid="Email address") is now validation-label; a field path (3.0 betas: vid="address.city") is now name (brackets for a native post: address[city]). check --fix picks by the value.