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
JavaScript
TypeScript
Rust
Python
Go
Swift
Kotlin
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
-
size'sm' | 'md' | 'lg''md' - Control size. `md` is the default; `sm` and `lg` scale the field height and padding.
-
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.
-
delimiterstring',' - Character that commits the current text as a tag when typed; pasted text is split on it.
-
max-tagsnumber0 - Maximum number of tags (0 = unlimited). At the limit, entry is disabled with a "-- max reached" hint.
-
allow-custombooleantrue - When false, only values from `suggestions` can be added; free text is rejected.
-
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.
-
readonlybooleanfalse - Prevents adding or removing tags while the field stays focusable and the tags still submit with the form.
-
formAssociatedbooleantrue -
propertiesobject{ required: { type: Boolean, reflect: true }, readonly: { type: Boolean, reflect: true }, } - Lit merges static properties up the prototype chain, so every consumer gets these without declaring them. `required` participates in constraint validation below; `readonly` reflects 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
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.