The #[Modelable] attribute designates a property in a child component that can be bound to from a parent component using wire:model.
Apply the #[Modelable] attribute to a property in a child component to make it bindable:
<?php // resources/views/components/⚡todo-input.blade.php
use Livewire\Attributes\Modelable;
use Livewire\Component;
new class extends Component {
#[Modelable] // [tl! highlight]
public $value = '';
};
?>
<div>
<input type="text" wire:model="value">
</div>Now the parent component can bind to this child component just like any other input element:
<?php // resources/views/components/⚡todos.blade.php
use Livewire\Component;
new class extends Component {
public $todo = '';
public function addTodo()
{
// Use $this->todo here...
}
};
?>
<div>
<livewire:todo-input wire:model="todo" /> <!-- [tl! highlight] -->
<button wire:click="addTodo">Add Todo</button>
</div>When the user types in the todo-input component, the parent's $todo property automatically updates.
Without #[Modelable], you would need to manually handle two-way communication between parent and child:
// Without #[Modelable] - manual approach
<livewire:todo-input
:value="$todo"
@input="todo = $event.value"
/>The #[Modelable] attribute simplifies this by allowing wire:model to work directly on the component.
#[Modelable] is perfect for creating custom input components that feel like native HTML inputs:
<?php // resources/views/components/⚡date-picker.blade.php
use Livewire\Attributes\Modelable;
use Livewire\Component;
new class extends Component {
#[Modelable]
public $date = '';
};
?>
<div>
<input
type="date"
wire:model="date"
class="border rounded px-3 py-2"
>
</div>{{-- Usage in parent --}}
<livewire:date-picker wire:model="startDate" />
<livewire:date-picker wire:model="endDate" />[!warning] Your component's root element cannot be a form control withwire:model. Wrap your input in a wrapper element like<div>. Livewire injectswire:modelandx-modelableon the root element to wire up the parent binding — a secondwire:modelon the same element creates a conflict.
The parent can use wire:model modifiers for timing and network control:
{{-- Live updates on every keystroke --}}
<livewire:todo-input wire:model.live="todo" />
{{-- Debounce updates --}}
<livewire:todo-input wire:model.live.debounce.500ms="todo" />
{{-- Throttle updates --}}
<livewire:todo-input wire:model.live.throttle.500ms="todo" />[!note] Event-based modifiers on components Event-based modifiers like.blur,.change, and.entercontrol DOM events on specific elements, not reactive component bindings. To control sync timing for modelable components, place these modifiers on the actual input element inside the child component: ``blade {{-- Parent --}} <livewire:todo-input wire:model="todo" /> {{-- Child component --}} <input wire:model.blur="value" />``
Here's a more complex example of a rich-text editor component:
<?php // resources/views/components/⚡rich-editor.blade.php
use Livewire\Attributes\Modelable;
use Livewire\Component;
new class extends Component {
#[Modelable]
public $content = '';
};
?>
<div>
<div
x-init="
// Initialize your rich text editor library here
editor.on('change', () => {
$wire.content = editor.getContent()
})
"
>
<!-- Rich text editor UI -->
</div>
</div>{{-- Usage --}}
<livewire:rich-editor wire:model="postContent" />[!warning] Only one modelable property per component Currently Livewire only supports a single #[Modelable] attribute per component, so only the first one will be bound.Use #[Modelable] when:
wire:modelFor more information about parent-child communication and data binding, see the Nesting Components documentation.