Laravel Livewireとは?インストールから使い方の基本までコード例で解説

Laravel入門

「Laravel Livewireとは何か」「何ができるのか」を知りたくて検索した人向けに、Livewireの位置づけから導入手順、カウンターコンポーネントやフォームバリデーションといった実際に動くコード例までをまとめて解説します。掲載しているコード・コマンドはすべてLaravel 13 + Livewire 4系(PHP 8.3)の環境で実行し、Featureテストで動作を確認したものです。

Laravel Livewireとは何か

Laravel Livewireは、PHPだけでインタラクティブなUIコンポーネントを構築できる、Laravel公式のフルスタックフレームワークです。Vue.jsやReactのようなJavaScriptフレームワークを別途学習しなくても、Bladeテンプレートと似た書き方のまま、ボタンクリックやフォーム入力に応じて画面の一部だけをサーバーサイドの処理で更新できます。

裏側ではAjaxリクエストが自動的に送信され、コンポーネントの状態(プロパティ)とサーバー側のPHPクラスが同期する仕組みです。SPAのような操作感を、JavaScriptをほとんど書かずに実現できる点がLivewire最大の特徴です。

Livewireの読み方・意味

「Livewire」は英語でそのまま「ライブワイヤー」と読みます。英単語のlive wireは本来「通電中の電線」を意味し、転じて「精力的でエネルギッシュな人」を指す慣用句としても使われます。Laravelの文脈では、この慣用句のニュアンスどおり「即座に反応する動的なUI」を作れることを表した名称です。

Laravel LivewireをLaravelプロジェクトに導入する

LivewireはComposerでインストールします。Laravel 10以降・PHP 8.1以降が対象です。

composer require livewire/livewire

Laravelのパッケージ自動検出に対応しているため、追加の設定は不要です。続いて、Livewireのアセットを読み込むレイアウトファイルを生成します。

php artisan livewire:layout

resources/views/layouts/app.blade.phpが生成され、<head>内に@livewireStyles</body>直前に@livewireScriptsが自動で配置されます。画面が正しく動かないときは、まずこの2つのディレクティブが両方とも配置されているかを確認してください。

<head>
    ...
    @livewireStyles
</head>
<body>
    {{ $slot }}

    @livewireScripts
</body>

はじめてのLivewireコンポーネント:カウンターを作る

Livewireの定番の入門例として、クリックのたびに数字が増えるカウンターコンポーネントを作成します。PHPクラスとBladeビューを別ファイルに分けるクラスベース構成にするには、--classオプションを付けて生成します。

php artisan make:livewire Counter --class

app/Livewire/Counter.phpが生成されます。名前空間はApp\Livewireです(Livewire 2系までのApp\Http\Livewireから変更されているため、古い記事のコードをそのまま使うと名前空間エラーになる点に注意してください)。

app/Livewire/Counter.php

<?php

namespace App\Livewire;

use Livewire\Component;

class Counter extends Component
{
    public int $count = 0;

    public function increment()
    {
        $this->count++;
    }

    public function render()
    {
        return view('livewire.counter');
    }
}

resources/views/livewire/counter.blade.php

<div>
    <h1>{{ $count }}</h1>
    <button wire:click="increment">Increment</button>
</div>

作成したコンポーネントは、Bladeビューの中で次のように呼び出します。

<livewire:counter />

ブラウザを開かなくても、Livewireの用意するテストヘルパーで動作を確認できます。以下のFeatureテストは実際に実行して成功することを確認済みです。

use App\Livewire\Counter;
use Livewire\Livewire;

test('カウンターがクリックのたびに増える', function () {
    Livewire::test(Counter::class)
        ->assertSet('count', 0)
        ->call('increment')
        ->assertSet('count', 1)
        ->assertSee(1);
});

データバインディング:wire:model と wire:model.live の違い

入力フォームの値をプロパティと同期させるにはwire:modelを使います。ここで誤解されやすいのが「wire:modelは常にリアルタイムで同期する」という点です。実際には、Livewire 3系以降のwire:modelデフォルトでは遅延評価で、フォーム送信やアクション呼び出しなど何らかのリクエストが発生するタイミングでまとめて同期されます。

<input type="text" wire:model="name">

入力のたびにサーバーへ問い合わせて即座に反映したい場合は、wire:model.liveを使います。この場合、デフォルトで250msのデバウンスがかかった状態でリクエストが送られます。

<input type="text" wire:model.live="email">

フォームバリデーションを実装する

入力途中でエラーメッセージを表示するリアクティブなバリデーションも、Livewireなら少ないコードで実現できます。

app/Livewire/ContactForm.php

<?php

namespace App\Livewire;

use Livewire\Component;

class ContactForm extends Component
{
    public string $name = '';
    public string $email = '';

    protected $rules = [
        'name' => 'required|min:2',
        'email' => 'required|email',
    ];

    public function updated($propertyName)
    {
        $this->validateOnly($propertyName);
    }

    public function submit()
    {
        $this->validate();
        // 保存処理など
    }

    public function render()
    {
        return view('livewire.contact-form');
    }
}

resources/views/livewire/contact-form.blade.php

<div>
    <form wire:submit="submit">
        <input type="text" wire:model="name" placeholder="お名前">
        @error('name') <span class="error">{{ $message }}</span> @enderror

        <input type="text" wire:model.live="email" placeholder="メールアドレス">
        @error('email') <span class="error">{{ $message }}</span> @enderror

        <button type="submit">送信</button>
    </form>
</div>

updated()ライフサイクルフックでvalidateOnly()を呼ぶことで、プロパティが更新されるたびに該当項目だけをバリデーションします。次のテストも実行して成功を確認済みです。

use App\Livewire\ContactForm;
use Livewire\Livewire;

test('不正なメールアドレスでエラーになる', function () {
    Livewire::test(ContactForm::class)
        ->set('email', 'invalid-email')
        ->assertHasErrors(['email' => 'email']);
});

test('正しい値なら送信できる', function () {
    Livewire::test(ContactForm::class)
        ->set('name', '露崎')
        ->set('email', 'test@example.com')
        ->call('submit')
        ->assertHasNoErrors();
});

コンポーネント間の通信:イベントの発行と購読

複数のコンポーネントで状態を連携させたい場合は、イベントの発行(dispatch)と購読(listen)を使います。Livewire 2系にあったprotected $listeners配列は、Livewire 3系以降では#[On]属性に置き換わっています。

// 発行側
public function notify()
{
    $this->dispatch('post-created', title: $post->title);
}
use Livewire\Attributes\On;

// 購読側
#[On('post-created')]
public function updatePostList($title)
{
    $this->latestTitle = $title;
}

この発行・購読の組み合わせも、Featureテストで実際に連携することを確認しています。

wire:textで画面の一部だけを即座に更新する

「いいね」ボタンのように、サーバーへの保存を待たずに見た目だけ先に反映したい場合は、Bladeの{{ }}展開の代わりにwire:textディレクティブが使えます。ネットワークの往復を待たずに要素のテキストを更新できるため、体感速度が向上します。

<div>
    <button x-on:click="$wire.likes++" wire:click="like">❤️ Like</button>
    Likes: <span wire:text="likes"></span>
</div>

x-on:click(Alpine.js)で即座に見た目のカウントを増やしつつ、wire:clickでサーバー側の保存処理も並行して呼び出す組み合わせです。

Livewire 4の新機能:シングルファイルコンポーネント

Livewire 4では、--classを付けずにmake:livewireを実行すると、PHPクラスとBladeビューを1つのファイルにまとめた「シングルファイルコンポーネント」がデフォルトで生成されます。

php artisan make:livewire pages::post.create

ファイル名の先頭に⚡マークが付き(エディタ上で一目でLivewireコンポーネントと分かるようにするための表示で、設定で無効化も可能)、ファイル内には無名クラスでロジックを記述します。

<?php

use Livewire\Component;

new class extends Component
{
    public string $title = '';
    public string $content = '';

    public function save()
    {
        $this->validate([
            'title' => 'required',
            'content' => 'required',
        ]);
        // 保存処理
    }
};
?>

<div>
    <form wire:submit="save">
        <input type="text" wire:model="title">
        @error('title') <span>{{ $message }}</span> @enderror

        <textarea wire:model="content"></textarea>
        @error('content') <span>{{ $message }}</span> @enderror

        <button type="submit">保存</button>
    </form>
</div>

今回紹介したクラスベースの書き方(--class付き)は引き続きサポートされているため、既存プロジェクトを書き換える必要はありません。新規に作るコンポーネントから少しずつシングルファイル形式を試す、という導入の仕方で問題ありません。

よくある質問

Q. wire:textとは何ですか?

A. サーバーとの通信結果を待たずに、指定した要素のテキスト内容だけを即座に書き換えるディレクティブです。Blade標準の{{ }}展開はコンポーネントの再描画(サーバーへの通信)を伴いますが、wire:textはその往復を省略できます。

Q. Laravel LivewireとJavaScriptフレームワーク(Vue.js・React)の違いは?

A. Vue.jsやReactはフロントエンドで状態管理とレンダリングを行いますが、Livewireは状態管理とレンダリングをサーバーサイドのPHPで行い、差分だけをブラウザに返します。フロントエンド専用の言語・ビルド環境を新たに学ぶ必要がなく、Laravelの知識だけで動的なUIを作れる点が異なります。

Q. 学習コストは高いですか?

A. Bladeテンプレートとコントローラーの書き方を理解していれば、wire:clickwire:modelといった基本のディレクティブだけで多くの画面が作れます。本記事のカウンターとフォームの例が動かせれば、基本的な使い方は押さえられています。

関連記事

Livewireの個別コマンドについては、以下の記事もあわせて参考にしてください。

Livewireをベースにした管理画面構築フレームワークであるFilamentについては、以下の記事で解説しています。

まとめ

Laravel Livewireは、PHPの知識だけでJavaScriptフレームワーク並みのインタラクティブなUIを構築できるツールです。導入自体はComposerで完結し、カウンターコンポーネントのような小さな例から始めれば、wire:clickwire:model・バリデーション・イベント通信といった主要な機能を短時間で一通り体験できます。Livewire 4で追加されたシングルファイルコンポーネントも既存の書き方と共存できるため、まずは本記事のクラスベース構成で試し、慣れてきたら新しい書き方も取り入れてみてください。

レン (Wren)

こんにちは。レンです。

Laravelのコードの森に住んでいる、小さな案内役です。
ルーティングの枝やクラスの影を歩きながら、コードの流れや仕組みを眺めています。

このサイトでは、Laravelの基本から実装のコツまで、開発で役立つポイントを静かに整理しています。
難しいことを増やすのではなく、コードの見通しが少し良くなるヒントを届けるのが役目です。

「この処理はどこに書くのがいいのか」
「Laravelではどう考えると整理できるのか」

そんな疑問に、小さなメモを残すような気持ちで記事を書いています。

コードを書いている途中で迷ったとき、
このサイトが少し立ち止まって整理できる場所になればうれしいです。

レン (Wren)をフォローする

コメント