Livewire: interactivity without JavaScript
A counter, a live search, a form validated on the fly usually need JavaScript and a separate API. Livewire lets you write all of that in PHP: a component keeps its state on the server, and the page updates by itself, without a reload.
Installation
composer require livewire/livewire
php artisan make:livewire counterThe command creates the app/Livewire/Counter.php class and the resources/views/livewire/counter.blade.php template.
The first component
namespace App\Livewire;
use Livewire\Component;
class Counter extends Component
{
public int $count = 0;
public function increment(): void
{
$this->count++;
}
public function render()
{
return view('livewire.counter');
}
}<div>
<button wire:click="increment">+1</button>
<span>{{ $count }}</span>
</div>The class's public properties are available in the template. A click on the button sends a request to the server, calls the method and updates the markup.
Putting it on a page
<livewire:counter />Binding a field
wire:model ties a form field to a property:
class PostSearch extends Component
{
public string $query = '';
public function render()
{
return view('livewire.post-search', [
'posts' => Post::where('title', 'like', "%{$this->query}%")->limit(10)->get(),
]);
}
}<div>
<input type="search" wire:model.live.debounce.300ms="query" placeholder="Search">
<ul>
@foreach ($posts as $post)
<li wire:key="{{ $post->id }}">{{ $post->title }}</li>
@endforeach
</ul>
</div>The word live updates the component while typing, and debounce.300ms waits for a pause so that a request is not sent for every letter.
A validated form
use Livewire\Attributes\Validate;
class CreatePost extends Component
{
#[Validate('required|min:5')]
public string $title = '';
#[Validate('required')]
public string $body = '';
public function save()
{
$this->validate();
Post::create(['title' => $this->title, 'body' => $this->body, 'user_id' => auth()->id()]);
return $this->redirect('/posts');
}
public function render()
{
return view('livewire.create-post');
}
}<form wire:submit="save">
<input type="text" wire:model="title">
@error('title') <p class="error">{{ $message }}</p> @enderror
<textarea wire:model="body"></textarea>
@error('body') <p class="error">{{ $message }}</p> @enderror
<button type="submit">
<span wire:loading.remove>Save</span>
<span wire:loading>Saving…</span>
</button>
</form>wire:loading shows an element while a request is in progress.
Which to choose
| Tool | When it fits |
|---|---|
| Blade with a little Alpine.js | Ordinary pages with a couple of interactive touches |
| Livewire | Interactive forms, filters, tables — without a separate front end |
| Inertia with React or Vue | A complex interface, a team with front-end developers |
Common mistake
A component's public properties are sent to the browser, and a visitor can change them before submitting. A property like $userId or $price cannot be trusted: check rights and data in every method, as in a regular controller. Properties the client must not change are marked with the #[Locked] attribute.