Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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 counter

The 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

ToolWhen it fits
Blade with a little Alpine.jsOrdinary pages with a couple of interactive touches
LivewireInteractive forms, filters, tables — without a separate front end
Inertia with React or VueA 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.

Thanks for reading!

Donate to the site — we work hard for you.