Wirestrap is still in active development
This project is pre-1.0 and may introduce breaking changes at any time without prior notice.
Autocomplete
Text input with a suggestion dropdown and inline ghost text.
The suggestion list comes from a Livewire method via wire-options,
called once on first focus and cached client-side. If a value is already bound
on load, the method is called immediately to resolve it.
Keyboard:
- ↑ ↓ to navigate,
- Tab, Enter or , to confirm,
- Escape to close.
Basic usage
Declare a method returning a list<string> and pass its name to
wire-options. Use icon to add an icon,
and floating for a floating label layout.
#[Renderless]
public function getCities(): array
{
return ['Paris', 'London', 'Berlin'];
}
{{-- Standard --}}
<x-wirestrap::autocomplete
wire:model="city"
wire-options="getCities"
label="City"
icon="bi-geo-alt"
/>
{{-- Floating label --}}
<x-wirestrap::autocomplete ... floating />
Icon prop
The icon prop accepts any class string from your preferred icon library,
or an array of attributes for full control. Custom icon components are also supported,
see icon configuration.
Multiple
Add multiple to enable tag mode. wire:model must
bind to an array property. Tags are synced via
$wire.$set() rather than through the input directly, so
wire:model.live has no effect here. Use the live
prop instead to trigger a Livewire request on each tag change.
Keyboard: Enter, Tab, or , to add a tag. Backspace on an empty input removes the last tag.
public array $tags = [];
<x-wirestrap::autocomplete
...
wire:model="tags"
wire-options="getTags"
multiple
/>
Add live to sync each tag change to Livewire immediately.
Without it, tags are deferred until the next Livewire request.
<x-wirestrap::autocomplete ... live />
Options
Options returned by wire-options can be plain strings or structured objects.
Plain strings serve as both the stored value and the display label.
Structured objects allow a separate label, groups via optgroup,
extra CSS via class, and visual prefixes or suffixes via
html_prefix / html_suffix.
Filtering always matches both label and value.
Ghost text only appears when the query matches the start of the value.
// Plain strings: the string is both the stored value and the display label
#[Renderless]
public function getCities(): array
{
return ['Paris', 'London', 'Berlin'];
}
// Structured objects: separate value, label, and optional fields
#[Renderless]
public function getContacts(): array
{
return [
[
'value' => '[email protected]',
'label' => 'Alice Martin',
'optgroup' => 'Recent',
'html_prefix' => '<span class="badge badge-primary me-2">Client</span>',
],
[
'value' => '[email protected]',
'label' => 'Frank Miller',
'optgroup' => 'All',
'html_prefix' => '<span class="badge badge-warning me-2">Partner</span>',
],
// ...
];
}
{{-- value = email, label = contact name; search works on both --}}
<x-wirestrap::autocomplete
wire:model="contact"
wire-options="getContacts"
label="To"
icon="bi-person"
/>
{{-- Multiple mode: tags display the label, the stored value is the email --}}
<x-wirestrap::autocomplete
wire:model="contacts"
wire-options="getContacts"
label="To"
icon="bi-people"
multiple
floating
/>
Dynamic options
Use wire-options-watch to invalidate the cache when a Livewire
property changes. Pass a string for cache invalidation only. Pass an array
[property, resetValue] to also reset the model on change.
To pass arguments to the method, use the array form of wire-options:
[method, ...params]. Use ['ws-wire' => 'dotPath'] to resolve
a Livewire property at call time.
To force a reload imperatively, use $wirestrap.autocomplete.refresh(id).
<x-wirestrap::select
...
wire:model="reactiveCategory"
wire-options="getReactiveCategories"
/>
<x-wirestrap::autocomplete
...
wire:model="reactiveItem"
{{-- Pass $this->reactiveCategory at call time --}}
:wire-options="['getReactiveItems', ['ws-wire' => 'reactiveCategory']]"
{{-- On category change: refresh suggestions and set $this->reactiveItem to null --}}
:wire-options-watch="['reactiveCategory', null]"
/>
Disabled
<x-wirestrap::autocomplete ... disabled />
Validation
The component checks $errors for the wire:model
key and applies the invalid state if found. Try submitting without typing
a city.
{{-- Suppress both the invalid class and the error message --}}
<x-wirestrap::autocomplete ... :has-validation="false" />
{{-- Keep the invalid class, hide the error message --}}
<x-wirestrap::autocomplete ... :has-validation-message="false" />
In multiple mode, individual invalid tags are highlighted. Enter any value that is not a valid email address to trigger a per-tag error.
Clipping
The dropdown uses position: absolute by default, which means it can
be clipped by a parent with overflow: hidden or confined to a CSS
stacking context. Two escape hatches are available:
-
teleport="body"moves the dropdown outside the component's DOM tree entirely to the given selector, via Livewire's@teleportdirective. It escapes all stacking contexts and still usesabsolutepositioning relative to the target, so there is no viewport recalculation on scroll. This is the recommended approach. -
position="fixed"positions the dropdown relative to the viewport, which escapes most clipping scenarios but not all. Every scroll event while the dropdown is open triggers a viewport recalculation. Preferteleportunless you have a reason to avoid moving the dropdown in the DOM.
{{-- Default: absolute positioning --}}
<x-wirestrap::autocomplete ... />
{{-- Escape most clipping scenarios, at the cost of viewport recalculations --}}
<x-wirestrap::autocomplete ... position="fixed" />
{{-- Move the dropdown outside the DOM tree entirely, escaping all stacking contexts --}}
<x-wirestrap::autocomplete id="my-autocomplete" ... teleport="body" />
Props
Global configuration
Most prop defaults can be overridden globally via config/wirestrap.php.
x-wirestrap::autocomplete
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string|null |
null |
Input id. Auto-resolved from wire:model when omitted. |
label |
ComponentSlot|string|null |
null |
Label text or slot content. No label element is rendered when null. |
wire-options |
string|list<mixed>|null |
null |
Livewire method returning the suggestions array — see Option definition below. String: method name. Array: [method, ...params] — remaining elements are passed as positional arguments; use ['ws-wire' => 'dotPath'] to resolve a Livewire property at call time. Called once on first focus and cached client-side. |
wire-options-watch |
string|list<mixed>|null |
null |
Livewire property to watch. Invalidates the cache on change; reloads on next focus or immediately if open. Array form [property, resetValue] also resets the model on change; third element true triggers a server round-trip on reset. Requires wire-options. |
icon |
string|array |
null |
Icon identifier forwarded to the configured icon component. |
icon-placement |
string |
'start' |
Icon position: start or end. |
placeholder |
string|null |
null |
Placeholder text. Forced to a space in floating mode. |
floating |
bool |
false |
Use floating label layout. |
min-chars |
int |
config |
Minimum number of characters required before suggestions appear. 0 shows all suggestions on focus. |
no-dropdown |
bool |
config |
Disable the suggestion dropdown entirely. Useful in multiple mode as a pure tag input with no suggestions. |
multiple |
bool |
false |
Enable tag mode. wire:model must bind to an array property. |
live |
bool |
false |
In multiple mode, sync to Livewire on each tag change instead of deferring. |
disabled |
bool |
false |
Disable the component. |
has-validation-message |
bool |
config |
Render the error message below the field. |
has-validation |
bool |
config |
Apply the invalid class when the wire:model key has an error. |
default-attributes |
array |
config |
Default HTML attributes merged onto the input element. |
dropdown-offset |
int |
config |
Y-axis offset in pixels between the trigger and the dropdown. |
position |
string |
config |
Dropdown positioning strategy: absolute or fixed. See Clipping. |
teleport |
string|null |
config |
CSS selector of the teleport target (e.g. body). Moves the dropdown outside the component's DOM tree, escaping all stacking contexts. Requires id. See Clipping. |
|
No results
|
|||
Option definition
| Prop | Type | Default | Description |
|---|---|---|---|
value |
string |
-- |
The value stored and injected into the input on selection. |
label |
string |
'' |
Display text. Always rendered as plain text. Filtering matches both label and value. |
optgroup |
string |
null |
Group label. Options sharing the same value are rendered together. Options without optgroup appear after all groups. |
class |
string |
null |
Extra CSS class applied to the option element and the tag in multiple mode. |
html_prefix |
string |
null |
Raw HTML injected before the label. Never pass user-generated content without escaping it first. |
html_suffix |
string |
null |
Raw HTML injected after the label. Same XSS warning applies. |
|
No results
|
|||
$wirestrap.autocomplete
| Method | Type | Default | Description |
|---|---|---|---|
$wirestrap.autocomplete.refresh(id) |
-- |
-- |
Invalidate the cached list and call wire-options immediately. id is the input id (auto-resolved from wire:model when omitted). |
|
No results
|
|||