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
With Preexisting Tags
With Max 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
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.
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"witharia-labelmatching the label text - Each tag chip uses
role="listitem"inside arole="list"container - Remove buttons have
aria-label="Remove {tag}"for screen readers - Tag creation and removal are announced via
aria-live="polite"region - When
maxTagsis reached, the input getsaria-disabled="true"and a polite announcement aria-invalid="true"andaria-describedbyset 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],
],