こんにちは、かつコーチです。
前回は、Bladeテンプレートの基本文法を解説しました。
@if や @foreach、@extends などを使えば、動的なHTMLはひと通り書けるようになります。
ただ、実際に画面を作っていくと、同じ見た目のパーツを何度もコピペしている自分に気づくはずです。
アラートメッセージ、カード型のUI、ボタンなど、「見た目は同じで中身だけ違う」パーツは、Bladeのコンポーネントを使うことでスッキリまとめられます。
今回は、このBladeコンポーネントの作り方と使いどころを解説します。
Bladeコンポーネントとは?
再利用可能なパーツを部品化する仕組み
Bladeコンポーネントとは、HTMLの一部分を「部品」として切り出し、<x-alert> のようなタグ1つで呼び出せるようにする機能です。
たとえば「成功メッセージ」「警告メッセージ」を表示するアラートボックスを、いろいろな画面で使い回すケースを考えてみましょう。
コンポーネントを使わないと、こうなりがちです。
{{-- ユーザー登録画面 --}}
<div class="alert alert-success">
<strong>登録が完了しました。</strong>
</div>
{{-- 投稿画面 --}}
<div class="alert alert-success">
<strong>投稿を保存しました。</strong>
</div>
見た目のクラス名やHTML構造を、ページごとに毎回コピペしています。
これをコンポーネント化すると、次のように1行で呼び出せます。
<x-alert type="success">登録が完了しました。</x-alert>
デザインを変更したくなったときも、コンポーネントのファイルを1箇所直すだけで、すべての呼び出し箇所に反映されます。
includeとの違い
Bladeには元々 @include というパーツ読み込みの仕組みもあります。
「同じようなことができるなら、コンポーネントとどう違うの?」と疑問に思うかもしれません。
大きな違いは、データの渡し方の柔軟さと属性の扱いやすさです。
@include は配列で変数を渡す必要がありますが、コンポーネントはHTMLの属性のような直感的な書き方でデータを渡せます。
さらにコンポーネントは、クラスを使ったロジックの記述や、スロットによる柔軟な中身の差し込みにも対応しています。
単純な部分テンプレートの読み込みなら @include、ロジックを持たせたい・繰り返し使う部品ならコンポーネント、というのが基本的な使い分けの目安です。
コンポーネントの作り方
artisanコマンドで作成する
コンポーネントは、artisanコマンドで雛形を作るのが基本です。
php artisan make:component Alert
これを実行すると、2つのファイルが作られます。
app/View/Components/Alert.php(ロジックを書くクラス)resources/views/components/alert.blade.php(見た目を書くテンプレート)
シンプルな見た目だけのコンポーネントであれば、クラスファイルなしで匿名コンポーネントとして作ることもできます。
php artisan make:component Alert --view
このコマンドだと resources/views/components/alert.blade.php だけが作成されます。
「ロジックはいらず、HTMLをまとめたいだけ」というケースでは、こちらの方が管理するファイルが少なく済みます。
基本構造を書く
まずはシンプルな匿名コンポーネントから見ていきましょう。
resources/views/components/alert.blade.php に、次のように書きます。
{{-- resources/views/components/alert.blade.php --}}
<div class="alert alert-{{ $type }}">
<strong>{{ $slot }}</strong>
</div>
呼び出す側は、以下のように書きます。
<x-alert type="success">登録が完了しました。</x-alert>
type="success" の部分がコンポーネント側の $type に、タグの中に書いたテキストが $slot に、それぞれ渡されます。
コンポーネントのファイル名(alert.blade.php)が、そのまま呼び出しタグの名前(<x-alert>)になる点がポイントです。
propsでデータを渡す
属性でデータを渡す
先ほどの type="success" のように、コンポーネントタグの属性として渡した値は、コンポーネント側で変数としてそのまま使えます。
複数の値を渡す場合も、属性を並べるだけです。
<x-alert type="danger" title="エラー">
入力内容を確認してください。
</x-alert>
{{-- resources/views/components/alert.blade.php --}}
<div class="alert alert-{{ $type }}">
<p class="alert-title">{{ $title }}</p>
<p>{{ $slot }}</p>
</div>
クラスベースのコンポーネント(--view を付けずに作成したもの)の場合は、コンストラクタでpropsを明示的に受け取ります。
<?php
// app/View/Components/Alert.php
namespace App\View\Components;
use Illuminate\View\Component;
class Alert extends Component
{
public string $type;
public function __construct(string $type = 'info')
{
$this->type = $type;
}
public function render()
{
return view('components.alert');
}
}
コンストラクタで受け取った $type は、そのままBladeテンプレート側で {{ $type }} として使えます。
デフォルト値を設定しておけば、<x-alert>登録しました</x-alert> のように属性を省略した呼び出しにも対応できます。
$slotでタグの中身を差し込む
先ほどから登場している $slot は、コンポーネントタグの開始タグと終了タグの間に書いた内容がそのまま入る、特別な変数です。
<x-alert type="success">
<strong>保存が完了しました。</strong>詳細は一覧画面で確認できます。
</x-alert>
このように、タグの中に自由にHTMLを書いても、その内容がまるごと $slot に渡ります。
「文字列だけでなく、HTMLの構造ごと差し込める」という点が、単純な変数渡しとの大きな違いです。
つまずきやすいポイント:includeと使い分けを誤って混乱した話
「なんでも@includeでいい」と思っていた頃
私が最初にBladeコンポーネントを学んだとき、正直「@include で十分では?」と思っていました。
実際に、ボタンパーツを @include で作って、こんな風に運用していた時期があります。
❌ Before:@includeで無理やり部品化する
{{-- resources/views/parts/button.blade.php --}}
<button class="btn btn-{{ $color ?? 'primary' }}">
{{ $label ?? '送信' }}
</button>
{{-- 呼び出し側 --}}
@include('parts.button', ['color' => 'danger', 'label' => '削除する'])
見た目はコンポーネントと似ていますが、呼び出し側の書き方が配列だらけで読みにくく、どんな属性を渡せるのかもファイルを開かないと分かりませんでした。
さらに厄介だったのが、ボタンの中に「アイコン付きのテキスト」のようなHTML構造を差し込みたくなったときです。
@include の配列渡しでは文字列しか渡せないため、無理やり <i> タグを含んだ文字列を配列に埋め込むという、かなり無理のある書き方をしてしまいました。
✅ After:コンポーネント化してスロットで解決する
{{-- resources/views/components/button.blade.php --}}
<button {{ $attributes->merge(['class' => 'btn btn-' . ($color ?? 'primary')]) }}>
{{ $slot }}
</button>
{{-- 呼び出し側 --}}
<x-button color="danger">
<i class="icon-trash"></i> 削除する
</x-button>
コンポーネントにしたことで、呼び出し側のコードがHTMLタグとして自然に読め、アイコン付きのテキストも $slot にそのまま書けるようになりました。
「複数の場所で使い回す」「中身にHTML構造を含めたい」というパーツに出会ったら、@include ではなくコンポーネントを検討する、というのが今の私の判断基準です。
応用・一歩先の使い方
名前付きスロットで複数箇所に差し込む
コンポーネントの中に「タイトル部分」と「本文部分」のように、差し込みたい場所が複数あるケースもあります。
そんなときは名前付きスロットを使います。
{{-- resources/views/components/card.blade.php --}}
<div class="card">
<div class="card-header">
{{ $title }}
</div>
<div class="card-body">
{{ $slot }}
</div>
</div>
{{-- 呼び出し側 --}}
<x-card>
<x-slot:title>
お知らせ
</x-slot:title>
今月のメンテナンス予定についてご案内します。
</x-card>
<x-slot:title> で渡した内容がコンポーネント側の $title に、それ以外の部分が通常通り $slot に入ります。
コンポーネントの置き場所を整理するルール
コンポーネントが増えてくると、resources/views/components/ の中がごちゃつきがちです。
私のチームでは、次のようなフォルダ分けのルールを決めて運用しています。
| 分類 | 例 | 置き場所 |
|---|---|---|
| フォーム系 | ボタン、入力欄 | components/form/ |
| 表示系 | アラート、バッジ | components/ui/ |
| レイアウト系 | ヘッダー、フッター | components/layout/ |
フォルダを分けた場合、呼び出し側のタグ名も <x-form.button> のようにドット区切りで指定します。
最初から完璧な分類を決める必要はありませんが、コンポーネントが10個を超えてきたあたりで一度整理しておくと、後々の見通しがよくなります。
まとめ
この記事のポイント
- Bladeコンポーネントは、HTMLパーツを
<x-alert>のようなタグとして再利用できる仕組み php artisan make:componentでクラス版・匿名版(--view)を作成できる- 属性で値を渡し、
$slotでタグの中身をまるごと差し込める - 複数箇所に差し込みたい場合は名前付きスロット(
<x-slot:title>)を使う - 単純な部分テンプレートは
@include、HTML構造ごと再利用したいパーツはコンポーネントが向いている
次に読むべき記事
コンポーネントで画面のパーツを整理できるようになったら、次はユーザーからの入力を受け取る処理を学んでいきましょう。
→ 次の記事:Requestオブジェクトでフォームの値を受け取る