Modelable

New Laracasts series
Master everything new in Livewire v4
Start learning

The #[Modelable] attribute designates a property in a child component that can be bound to from a parent component using wire:model.

Basic usage

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]
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" />
 
<button wire:click="addTodo">Add Todo</button>
</div>

When the user types in the todo-input component, the parent's $todo property automatically updates.

How it works

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.

Building reusable input components

#[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" />

Your component's root element cannot be a form control with wire:model. Wrap your input in a wrapper element like <div>. Livewire injects wire:model and x-modelable on the root element to wire up the parent binding — a second wire:model on the same element creates a conflict.

Modifiers

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" />
Caleb’s Note

Event-based modifiers like .blur, .change, and .enter control 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:

<livewire:todo-input wire:model="todo" />
 
<input wire:model.blur="value" />

Example: Custom rich text editor

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" />

Limitations

Only one modelable property per component

Currently Livewire only supports a single #[Modelable] attribute per component, so only the first one will be bound.

When to use

Use #[Modelable] when:

  • Creating reusable input components (date pickers, color pickers, rich text editors)
  • Building form components that need to work with wire:model
  • Wrapping third-party JavaScript libraries as Livewire components
  • Creating custom inputs with special validation or formatting

Learn more

For more information about parent-child communication and data binding, see the Nesting Components documentation.