Skip to main content
WireKit
Copy for LLM

Tags Input

The <x-wirekit::tags-input> component allows free-form tag creation. Users type text and press Enter or comma to create a tag chip. Each chip has a remove button, and the combined values are submitted as an array.

Basic Usage

Basic Tags Input

With Preexisting Tags

Preset Tags

With Max Tags

Maximum 3 Tags

You can add up to 3 keywords.

When the maximum is reached, the text input is disabled and a screen-reader announcement confirms the limit.

Error State

Tags input with an error message

At least one tag is required.

Width

Like all WireKit form components, the tags input fills its container (w-full). Control the width via the parent element:

<div class="max-w-md">
    <x-wirekit::tags-input label="Skills" name="skills" />
</div>

See Input — Width for more layout examples (grid columns, mixed widths).

Keyboard Behavior

Key Action
Enter Create tag from current text
, (comma) Create tag from current text
Backspace Remove last tag (when input is empty)
Escape Clear current input text

Duplicate tags are silently ignored — the same tag cannot appear twice.

Form Submission

Each tag is submitted via a hidden <input type="hidden" name="name[]">, so Laravel receives a plain array of strings.

Optimistic UI

Pass the name of the Livewire method the field should call and the set is sent the moment a tag is added or removed:

<x-wirekit::tags-input
    name="labels"
    label="Labels"
    :value="$labels"
    optimistic="saveLabels"
/>

Load wirekit-optimistic.js alongside whichever bundle you already use — below it, in your layout:

@wirekitScripts
<script src="{{ asset('vendor/wirekit/wirekit-optimistic.js') }}"></script>

Try it

The demo below runs the real path: the change shows immediately, the outline says it is provisional, and the server's answer either confirms it silently or takes it back.

Optimistic tags — accepted, refused, and a slow answer

The <livewire:demos.…> wrapper above exists only on this site — it supplies the demo methods so the page can show a real round trip. The block under it is what you write.

A refusal keeps the change rather than undoing it. A tag is typed, so undoing a failed add would delete what you just wrote. Removal takes the same exit for a structural reason: the set is one value, so a rollback restores the whole set — there is no way to undo a removal without also undoing an addition that happened alongside it. A refused removal therefore stays removed and says so, which is recoverable: the tag is one keystroke away.

The field is not marked invalid. aria-invalid means this value is wrong, and a save that failed on the network says nothing about the tags.

Props

Prop Type Default Description
label string|null null Label text above the input
hint string|null null Help text below the input
error string|null null Error message (also reads from $errors)
name string|null null Form field name (submits as name[])
id string|null auto-generated Element id
value array [] Initial tags
maxTags int|null null Maximum number of tags (null = unlimited)
placeholder string 'Add a tag...' Placeholder text in the text input
disabled bool false Disabled state
scope string|null null Scoped personalization key
optimistic string|null null Livewire method to call when a tag is added or removed, showing the new set before the server answers. A refusal keeps the change and says it was not saved — a rollback would delete text you typed. See Optimistic UI.
optimisticArgs array [] Extra arguments appended to the optimistic action call, after the new value — the row this control belongs to.

Accessibility

  • The input area uses role="group" with aria-label matching the label text
  • Each tag chip uses role="listitem" inside a role="list" container
  • Remove buttons have aria-label="Remove {tag}" for screen readers
  • Tag creation and removal are announced via aria-live="polite" region
  • When maxTags is reached, the input gets aria-disabled="true" and a polite announcement
  • aria-invalid="true" and aria-describedby set on error or hint
  • All chip remove icons are aria-hidden="true" — the button label provides the accessible name

Keyboard Interaction

Key Action
Tab Move focus to the tags input
Type characters Build the next tag
Enter / , (comma) Commit the typed text as a new tag
Backspace (in empty input) Remove the last tag
ArrowLeft / ArrowRight (in empty input) Move a virtual caret across existing tags for targeted removal
Escape Cancel the in-progress tag

Pitfalls

  • Don't use tags-input for a closed taxonomy. If the user must pick from a fixed list, <x-wirekit::multi-select> is correct. Tags-input is for free-form entry (skills, keywords, emails).
  • Don't allow duplicates without thought. The component dedupes by string equality — case-sensitive. If you want case-insensitive dedup, normalize on the server before persisting.

Design Tokens

Token Purpose
--color-wk-bg-input Input area background
--color-wk-bg-muted Tag chip background
--color-wk-border-strong Default border color
--color-wk-border-error Border color on error
--color-wk-text Input and tag text color
--color-wk-text-muted Placeholder and remove icon color
--color-wk-ring Focus ring color
--radius-wk-md Input container border radius
--radius-wk-sm Tag chip border radius
--transition-wk-duration Add/remove chip transition
--opacity-wk-disabled Dimmed state when disabled

Customization

Override defaults in config/wirekit.php:

'components' => [
    'tags-input' => ['placeholder' => 'Type and press Enter...', 'maxTags' => 10],
],

Further Reading

Was this page helpful?

Thanks — that helps.

Voting requires cookies or local storage. What we store