Tag Input
Free-text token entry field with optional autocomplete suggestions, delimiter splitting, and duplicate rejection.
<arc-tag-input>Overview
TagInput lets users build a list of free-text values as removable tag chips. Typing a value and pressing Enter, or the configurable delimiter character (comma by default), commits the trimmed text as a tag. Pasting text containing delimiters splits it into multiple tags at once, which makes importing comma-separated lists fast.
An optional suggestions array turns the field into a lightweight autocomplete: as the user types, matching suggestions appear in a dropdown listbox navigable with ArrowUp/ArrowDown and committed with Enter or a click. Free text is still allowed alongside suggestions unless allowCustom is set to false, in which case only values from the suggestion list can be added. Duplicate entries are rejected, and the existing chip shakes briefly to show why nothing was added (the animation is suppressed under reduced-motion preferences).
TagInput is form-associated: it submits one FormData entry per tag under its name, so servers receive the values as a repeated field. A maxTags limit disables further entry with a "-- max reached" hint once reached. Keyboard editing mirrors MultiSelect: Backspace in an empty input removes the last tag, and ArrowLeft from the start of the input walks focus into the chips where arrows navigate and Backspace/Delete removes.
Guidelines
When to use
- Always provide a `label` so the field is accessible to screen readers
- Provide `suggestions` when a known vocabulary exists. It speeds entry and reduces typos
- Set `allowCustom` to false when values must come from a controlled vocabulary
- Use `maxTags` to cap entries when downstream systems limit how many values are accepted
- Listen to `arc-input` to fetch or refine suggestions from a server as the user types
When not to use
- Do not use TagInput when values must be chosen from a fixed list and casual browsing matters. Use MultiSelect instead
- Do not pick a delimiter character that legitimately appears inside your values
- Do not use extremely long tag values. They will overflow the chips
- Do not rely on the shake animation alone to explain rejected input in critical flows. Pair with an `error` message where it matters
Features
- Free-text tag creation on Enter or a configurable delimiter character (comma by default)
- Paste splitting: pasted text containing delimiters becomes multiple tags in one action
- Optional autocomplete dropdown driven by a `suggestions` array with type-ahead filtering
- `allowCustom={false}` restricts entry to suggestion values only
- Duplicate rejection with a brief shake animation on the existing chip (respects reduced motion)
- `maxTags` limit with an inline "-- max reached" hint when full
- Full keyboard editing: Backspace removes the last tag, ArrowLeft walks into chips, arrows navigate, Backspace/Delete removes, Escape returns to the input
- Form-associated: submits one FormData entry per tag under `name`
Preview
Usage
This component requires JavaScript. No pure HTML/CSS version is available. Use the Web Component directly or a framework wrapper.
<arc-tag-input
label="Topics"
placeholder="Add a topic..."
suggestions='["JavaScript","TypeScript","Python"]'
max-tags="5"
></arc-tag-input>import { TagInput } from '@arclux/arc-ui-react';
export default function Example() {
return (
<TagInput
label="Topics"
placeholder="Add a topic..."
suggestions={['JavaScript', 'TypeScript', 'Python']}
maxTags={5}
onArcChange={(e) => console.log(e.detail.value)}
/>
);
}<script setup>
import { TagInput } from '@arclux/arc-ui-vue';
</script>
<template>
<TagInput
label="Topics"
placeholder="Add a topic..."
:suggestions="['JavaScript', 'TypeScript', 'Python']"
:maxTags="5"
/>
</template><script>
import { TagInput } from '@arclux/arc-ui-svelte';
</script>
<TagInput
label="Topics"
placeholder="Add a topic..."
suggestions={['JavaScript', 'TypeScript', 'Python']}
maxTags={5}
/>import { Component } from '@angular/core';
import { TagInput } from '@arclux/arc-ui-angular';
@Component({
imports: [TagInput],
template: `
<arc-tag-input
label="Topics"
placeholder="Add a topic..."
[suggestions]="['JavaScript', 'TypeScript', 'Python']"
[maxTags]="5"
/>
`,
})
export class MyComponent {}import { TagInput } from '@arclux/arc-ui-solid';
export default function Example() {
return (
<TagInput
label="Topics"
placeholder="Add a topic..."
suggestions={['JavaScript', 'TypeScript', 'Python']}
maxTags={5}
/>
);
}import { TagInput } from '@arclux/arc-ui-preact';
export default function Example() {
return (
<TagInput
label="Topics"
placeholder="Add a topic..."
suggestions={['JavaScript', 'TypeScript', 'Python']}
maxTags={5}
/>
);
}API
delimiterstring','- Character that commits the current text as a tag when typed; pasted text is split on it.
labelstring''- Visible label rendered above the field in a small uppercase style.
placeholderstring''- Hint text shown inside the field when no tags exist and the input is empty.
namestring''- Form field name. Each tag is submitted as its own FormData entry under this name.
disabledbooleanfalse- Disables the control, preventing interaction and reducing opacity to 50%.
errorstring''- Error message shown below the field; also applies error styling to the border.
valuestring[][]- Array of current tags. Updated on add/remove and emitted via
arc-change. suggestionsstring[][]- Autocomplete candidates. When non-empty, typing filters them into a dropdown listbox.
maxTagsnumber0- Maximum number of tags (0 = unlimited). At the limit, entry is disabled with a "-- max reached" hint.
allowCustombooleantrue- When false, only values from
suggestionscan be added; free text is rejected. readonlybooleanfalse- Prevents adding or removing tags while the field stays focusable and the tags still submit with the form.
size'sm' | 'md' | 'lg''md'- Control size.
mdis the default;smandlgscale the field height and padding. formAssociatedbooleantruepropertiesobject{ // flag(), unlike `disabled`. The exclusion in props.js is specifically // about form-associated *platform* semantics: a `disabled` content // attribute that is merely present makes the element actually disabled // per the HTML spec, and formDisabledCallback assigns the property back, // so no converter can win. Neither of these is platform-mapped: // `required` is enforced by _computeValidity() below and `readonly` by // each component's own interaction handlers, so the stock converter buys // nothing here and costs the usual bug: `required="false"` read as true, // blocking submission of a form the author meant to leave optional. // Finding #48's shape, across all 26 form controls at once. required: flag(false), readonly: flag(false), }- Lit merges static properties up the prototype chain, so every consumer
gets these without declaring them.
requiredparticipates in constraint validation below;readonlyreflects for styling and is enforced by each component's interaction handlers (the mixin can't know which gestures mutate state). autoValidatesbooleantrue- Components that run their own constraint-validation logic (pattern checks, range checks) opt out of the automatic required sync by overriding this to false, and own the whole validity flag set instead.
formvalidityvalidationMessagerequiredbooleanfalse
Methods
checkValidity()boolean- Whether the control currently satisfies its constraints, per the native
constraint-validation API. Fires
invalidon the element when it does not, and reports nothing to the user. reportValidity()boolean- As checkValidity(), but also shows the browser's validation message against the control when it fails.
Events
arc-changedetail:{ value: string[] }- Fired when a tag is added or removed; detail contains
{ value } arc-input- Fired as the user types; detail contains
{ query }
See Also
- Multi SelectMulti-value select with tag chips, inline search filtering, and keyboard navigation.
- ComboboxSearchable dropdown with type-ahead filtering.
- InputVersatile form control supporting single-line text, email, password, and multiline textarea modes with built-in label, placeholder, and validation states. Pairs with Form for complete data-entry workflows.
- TagCompact pill-shaped label with color variants, custom color support, and an optional remove button, for categorisation, filtering, and selection feedback.