Inputs
Tags Input
Free-form tags, typed into one field.
import { TagsInput } from 'omaris' Examples
Basic
Type and press Enter, a comma or Tab. Backspace in the empty box selects the last tag, and a second press takes it off.
<script lang="ts"> import { TagsInput } from 'omaris'; let keywords = $state(['kebab', 'grill']);</script><TagsInput class="w-96 max-w-full" label="Keywords" placeholder="Add a keyword" supportingText="Helps people find the dish in search." bind:value={keywords}/> Validation
validate returns the reason a tag was refused, and the typed text stays put for fixing. A paste with commas or newlines is split and vetted piece by piece; max adds a count and stops at the cap.
<script lang="ts"> import { TagsInput } from 'omaris'; let invites = $state(['dara@rasil.app']); const email = (tag: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(tag) || 'That is not an email address.';</script><TagsInput class="w-96 max-w-full" label="Invite teammates" placeholder="name@company.com" max={5} validate={email} bind:value={invites}/> In a field
Inside a Field it takes the label, the help and the error from the field, like any other control. The variants and sizes are Input's.
Shown as badges on the menu item.
Add at least one.
<script lang="ts"> import { Field, TagsInput } from 'omaris'; let diets = $state<string[]>([]);</script><Field class="w-96 max-w-full" label="Dietary tags" description="Shown as badges on the menu item." error={diets.length === 0 ? 'Add at least one.' : undefined} required> <TagsInput variant="filled" placeholder="vegan, halal…" bind:value={diets} /></Field> Overridden
A narrow box and long tags: a tag truncates inside its chip rather than pushing the field wider. classes reaches the frame, each chip and the box.
<script lang="ts"> import { TagsInput } from 'omaris';</script><TagsInput class="w-60 max-w-full" label="Paths" value={['/var/lib/omaris/cache/thumbnails', '/tmp']} classes={{ field: 'rounded-shape-xl', chip: 'font-mono', input: 'font-mono' }}/> When to use it
Use it for
- Free-form values the person types — keywords, labels, email recipients, allowed domains — bound as a
string[]withbind:value. - A list that is vetted as it grows:
validatereturns the reason a tag was refused and leaves the text in the box to fix;unique(on by default) refuses a repeat;maxcaps it and shows a count. - Pasting a list from somewhere else — a comma- or newline-separated paste becomes one tag per piece, each vetted on its own.
- A form field like any other: inside a Field, or with its own
labelandsupportingText, inInput's variants and sizes.
Not for
Do
- Say what separates tags in the
placeholderorsupportingText— "Separate with commas" — the first time it matters. - Return a specific reason from
validate— "Not an email address", not "Invalid" — and keep it short; it shares a line with the count. - Pass
namein a plain form: every tag posts as its own value under it. - Localise
removeLabel,duplicateMessageandmaxMessagewith the rest of the page's strings.
Don't
- Use it for values that must come from a fixed set; a typed tag that the server then rejects is worse than a list that never offered it.
- Put more than a line or two of tags in it — past about fifteen, the field is a list and wants to be a List with an add row.
- Set
requiredexpecting the browser to enforce it; the box is empty whenever a tag has been added. Checkvalue.lengthyourself.
Quick reference
size smdefault(default)lg
API
TagsInput
Free-form tags, typed into one field.
The field is an Input frame with the tags sitting in front of the text box, so it reads as one control and wraps onto a second row when it has to. Enter, a comma or Tab turns the text into a tag; a paste with commas or newlines in it becomes several at once.
Backspace in an empty box selects the last tag, and a second press takes it off — so a stray press never deletes something you can't see going. The arrows walk the tags from there, and Delete or Backspace removes the one you're on.
For picking from a fixed list rather than typing, see MultiSelect.
import { TagsInput } from 'omaris' <TagsInput bind:value={tags} label="Keywords" max={8} /><TagsInput bind:value={emails} validate={(t) => t.includes('@') || 'Not an email.'} /> Parts
Every element this renders is reachable from outside. Pass Tailwind for one part as classes={{ part: '…' }}; class covers the root and is merged last, so it always wins.
root- Wrapper: label, field, supporting text.
field- The frame: border, background, height, adornment layout.
control- The bare <input>; the frame owns every decoration.
label- No description in the source yet.
adornment- Icon slots at either end of the frame.
affix- Flat text glued to the value — "https://", "kg".
action- An interactive trailing control: reveal, clear.
support- Helper line under the field, and the counter beside it.
counter- No description in the source yet.
list- The tags and the text box, wrapped inside the frame.
chip- One tag — a
Chip, so it is the library's chip and not a lookalike. input- The bare text box the tags sit in front of.
Props
value bindableDefaults to []
string[] The tags, in the order they were added. Bindable.
query bindableDefaults to ''
string What is typed but not yet a tag. Bindable — handy for suggestions.
variant Defaults to 'outline'
InputVariant size Defaults to 'default'
InputSize smdefaultlg
label string Label above the field. Wired to the text box, so clicking it focuses.
supportingText string Helper line under the field. A rejected tag's reason replaces it.
invalid boolean Marks the field invalid. Inside a Field, defaults to the field's.
required boolean Stars the label. Inside a Field, defaults to the field's.
max number Most tags it takes. Adds an n/max count under the field.
unique Defaults to true
boolean Refuse a tag that is already there, ignoring case.
validate (tag: string, value: string[]) => string | true Vets a tag before it goes in. Return true to accept it, or the reason it was refused — that text shows under the field and the typed text stays put for fixing.
duplicateMessage Defaults to 'Already added.'
string Shown when unique refuses a tag.
maxMessage string Shown when a tag would go past max. Defaults to "Up to {max}."
removeLabel Defaults to (tag: string) => `Remove ${tag}`
(tag: string) => string Accessible name of a tag's ×.
onchange (value: string[]) => void Fires with the new list after every add and remove.
class string classes TagsInputClasses Per-part Tailwind overrides. class still covers the root.
ref bindableDefaults to null
HTMLInputElement | null