Copy-pasting the same chunk of HTML across many pages is a source of bugs and repetitive work. Blade components solve this: you define a piece of UI once, then use it anywhere like a normal HTML tag. This article covers how to build genuinely reusable UI components in Laravel 10/11 — from a simple alert to flexible buttons and cards.
1. Two Kinds of Components
Laravel has two types of Blade components:
- Class components — have a PHP class for logic (computing values, queries, etc.) plus a view file. Ideal for components that need processing.
- Anonymous components — just a Blade file, no class. Ideal for purely presentational components like buttons or cards.
2. Creating a Class Component
Use artisan to create an alert component:
php artisan make:component Alert
This produces two files: the class app/View/Components/Alert.php and the view resources/views/components/alert.blade.php. Define properties in the constructor:
class Alert extends Component
{
public function __construct(
public string $type = 'info',
public string $message = '',
) {}
public function render(): View
{
return view('components.alert');
}
}
Fill in the view, using the properties you just defined:
<div class="alert alert-{{ $type }}">
{{ $message }}
</div>
Use it on any page with the x- syntax:
<x-alert type="success" message="Data saved successfully." />
3. Anonymous Components with @props
For components with no logic, just create a Blade file — no class. Create resources/views/components/card.blade.php and declare its properties with the @props directive:
@props(['title' => null])
<div class="card">
@if ($title)
<div class="card-header">{{ $title }}</div>
@endif
<div class="card-body">
{{ $slot }}
</div>
</div>
@props registers which attributes are "props" (with default values); the rest fall into the attribute bag we discuss later.
4. Slots: Passing HTML Content
Notice {{ $slot }} above. A slot is where the content between the component tags is rendered. Now the card component can wrap anything:
<x-card title="User Profile">
<p>Name: Budi</p>
<p>Email: budi@mail.com</p>
</x-card>
Named Slots
Need more than one content area? Use named slots. Define them in the component:
@props(['title' => null])
<div class="card">
<div class="card-header">{{ $title }}</div>
<div class="card-body">{{ $slot }}</div>
<div class="card-footer">{{ $footer ?? '' }}</div>
</div>
Then fill them with x-slot tags:
<x-card title="Summary">
Main content goes here.
<x-slot:footer>
<button>Save</button>
</x-slot:footer>
</x-card>
5. The Attribute Bag: Forwarding HTML Attributes
What if the component's user wants to add a class or other attribute? Laravel collects every attribute not registered in @props into the $attributes variable. Use merge() so custom classes combine with the defaults:
@props(['type' => 'primary'])
<button {{ $attributes->merge(['class' => 'btn btn-'.$type]) }}>
{{ $slot }}
</button>
Now the button component can accept extra attributes seamlessly:
<x-button type="danger" class="w-full" onclick="remove()">
Delete
</x-button>
As a result, the w-full class and the onclick attribute automatically attach to the button tag without you declaring them. For attributes that should be overridden rather than merged, use $attributes->class([...]).
6. Class vs Anonymous: When to Use Which
- Use anonymous components for purely presentational elements (button, card, badge, input) — they are lighter and
@propsis enough. - Use class components when you need logic: fetching data from the database, formatting values, or choosing a variant based on complex conditions. You can also add public methods callable directly in the view.
Example of a method on a class component:
public function isDanger(): bool
{
return $this->type === 'danger';
}
@if ($isDanger())
<strong>Warning!</strong>
@endif
Conclusion
Blade components change how you build interfaces: instead of copying HTML, you create a consistent, maintainable component library. Start with anonymous components for simple elements, graduate to class components when you need logic, use slots for content flexibility, and the attribute bag to keep components open for customization. Once built cleanly, these components speed up every new page you create.