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, making it fast to import comma-separated lists.
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 — 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. -
formAssociatedbooleantrue -
propertiesobject{ // 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.
-
form -
validity -
validationMessage -
requiredbooleanfalse
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 Select Multi-value select with tag chips, inline search filtering, and keyboard navigation.
- Combobox Searchable dropdown with type-ahead filtering.
- Input Versatile 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.
- Tag Compact pill-shaped label with color variants, custom color support, and an optional remove button, for categorisation, filtering, and selection feedback.