Hướng dẫn sử dụng cú pháp `<x-...>` trong Laravel Blade
Cú pháp `<x-...>` (Component Tag) là cách gọi và hiển thị các Blade Component theo phong cách thẻ HTML trong Laravel. Thay vì dùng `@include` hay `@extends`, bạn có thể viết giao diện dạng module trực quan, sạch sẽ và dễ tái sử dụng hơn.
1. Quy tắc Laravel tìm file component (File Mapping)
Mọi thẻ bắt đầu bằng <x-...> mặc định sẽ tìm file trong thư mục resources/views/components/:
| Thẻ component | Đường dẫn file tương ứng |
|---|---|
<x-button /> |
resources/views/components/button.blade.php |
<x-post-card /> |
resources/views/components/post-card.blade.php (tự khớp kebab-case) |
<x-form.input /> |
resources/views/components/form/input.blade.php (dấu chấm = thư mục con) |
<x-admin.table.row /> |
resources/views/components/admin/table/row.blade.php |
Lưu ý: Dấu hai chấm
:cũng tương đương dấu chấm:<x-form:input />giống<x-form.input />.
2. Truyền dữ liệu: Chuỗi tĩnh vs Biến PHP
Đây là quy tắc quan trọng nhất khi dùng cú pháp <x-...>:
- Không có dấu
:➔ Giá trị là chuỗi chữ thô (String). - Có dấu
:phía trước ➔ Giá trị là biến PHP, biểu thức logic, số hoặc mảng.
{{-- 1. Chuỗi tĩnh --}}
<x-alert type="success" message="Đã lưu thành công" />
{{-- 2. Biến PHP (bắt buộc có dấu :) --}}
<x-user-card :user="$user" :posts-count="$count" />
{{-- 3. Biểu thức logic / Boolean / Mảng --}}
<x-modal :is-open="false" :tags="['laravel', 'blade']" />
3. Khai báo Props và Hứng Attributes tự do
Khi viết một component (ví dụ resources/views/components/form/input.blade.php):
{{-- 1. @props: Khai báo các biến component chủ động nhận & giá trị mặc định --}}
@props([
'name',
'label',
'type' => 'text',
])
<div class="mb-4">
<label class="block font-medium">{{ $label }}</label>
{{-- 2. $attributes: Tự động hứng mọi thuộc tính HTML còn lại (id, placeholder, disabled, class...) --}}
<input
type="{{ $type }}"
name="{{ $name }}"
{{ $attributes->merge(['class' => 'w-full rounded border px-3 py-2']) }}
/>
</div>
Khi sử dụng:
<x-form.input
name="email"
label="Địa chỉ Email"
placeholder="nhap@email.com"
class="bg-gray-50"
required
/>
👉 placeholder, required và class bg-gray-50 sẽ tự động được $attributes->merge() gắn thẳng vào thẻ <input> mà bạn không cần khai báo thêm.
4. Truyền nội dung qua Slots
Slot cho phép bạn bọc nội dung HTML bên trong thẻ component.
4.1. Slot mặc định ($slot)
Nội dung đặt giữa thẻ mở và thẻ đóng sẽ tự động rơi vào biến {{ $slot }}:
{{-- components/card.blade.php --}}
<div class="rounded-lg border p-4 shadow">
{{ $slot }}
</div>
{{-- Cách dùng --}}
<x-card>
<h3 class="font-bold">Tiêu đề</h3>
<p>Nội dung chi tiết nằm ở đây...</p>
</x-card>
4.2. Named Slots (Slot có tên: <x-slot:...>)
Dùng khi component có nhiều khu vực hiển thị (Header, Body, Footer):
{{-- components/modal.blade.php --}}
<div class="modal">
<div class="modal-header">{{ $header }}</div>
<div class="modal-body">{{ $slot }}</div>
<div class="modal-footer">{{ $footer }}</div>
</div>
{{-- Cách dùng --}}
<x-modal>
<x-slot:header>
<h2 class="text-lg">Xác nhận xóa</h2>
</x-slot:header>
<p>Bạn có chắc muốn xóa bản ghi này?</p>
<x-slot:footer>
<button>Hủy</button>
<button>Đồng ý</button>
</x-slot:footer>
</x-modal>
5. Dùng làm Layout với <x-layout>
Thay vì cấu trúc @extends + @section kinh điển:
File Layout: resources/views/components/layout.blade.php
<!DOCTYPE html>
<html>
<head>
<title>{{ $title ?? 'Website' }}</title>
</head>
<body>
<main>{{ $slot }}</main>
{{ $scripts ?? '' }}
</body>
</html>
Trang con: resources/views/home.blade.php
<x-layout title="Trang chủ">
<h1>Xin chào các bạn!</h1>
<x-slot:scripts>
<script>console.log('Script riêng của trang home');</script>
</x-slot:scripts>
</x-layout>
6. Bảng tra cứu nhanh cú pháp
| Mục đích | Cú pháp |
|---|---|
| Gọi component thư mục gốc | <x-tên-file /> |
| Gọi component trong thư mục con | <x-thư-mục.tên-file /> |
| Truyền chuỗi tĩnh | title="Xin chào" |
| Truyền biến PHP / biểu thức | :user="$user", :is-active="true" |
| Khai báo biến nhận & default | @props(['variant' => 'primary']) |
| Gộp class & nhận attribute HTML | {{ $attributes->merge(['class' => '...']) }} |
| Nội dung chính giữa 2 thẻ | {{ $slot }} |
| Nội dung theo vùng chỉ định | <x-slot:tên-vùng>...</x-slot:tên-vùng> |