A Laravel Blade component lets you capture the repeating parts of your interface in one place and reuse them on every page. Instead of copy-pasting structures like buttons, cards, alerts or modals, you define them as components: you pass data in through props and keep the content flexible with slots. In this guide I walk through building Blade components from scratch, the difference between anonymous and class-based components, how props and slots work together, and practical tips you will actually use in real projects.
What is a Blade component and why use one?
Blade is Laravel's templating engine, and components are its reusable, self-contained pieces. A component has two parts: the tag you use from the outside (for example <x-alert />) and the Blade file behind it. The benefits are clear:
- Less repetition: you don't rewrite the same card on ten pages.
- Easier maintenance: when the design changes, you edit a single file.
- Better readability: the page template is made of meaningful tags.
- Testability: you can verify components in isolation.
Creating your first component
The fastest way to scaffold a component is with Artisan. The command below generates both a class and a view file:
php artisan make:component Alert
This creates two files: app/View/Components/Alert.php (the component class) and resources/views/components/alert.blade.php (the view). To use the component on a page, write its tag with the x- prefix:
<x-alert />
Folder structure is reflected with a separator instead of a slash. For example, the file resources/views/components/forms/input.blade.php is called as <x-forms.input />.
Passing data with props
Components usually receive data from the outside. In a class-based component you define this through constructor parameters. In the example below, type and message are props:
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class Alert extends Component
{
public function __construct(
public string $type = 'info',
public string $message = '',
) {}
public function render(): View
{
return view('components.alert');
}
}
In the view file you access these properties directly:
<div class="alert alert-{{ $type }}">
{{ $message }}
</div>
When using it, you pass props like HTML attributes. Use a plain attribute for static values and the : prefix for variables:
<x-alert type="danger" message="Record deleted." />
<x-alert type="success" :message="$notice" />
Attribute names are automatically converted to camelCase: if you write card-title in HTML, you receive it as $cardTitle in the class.
Keeping content flexible with slots
Props are ideal for simple values, but when you want to put HTML, other components or longer text inside a component, you use a slot. Everything between the opening and closing tags lands in the {{ $slot }} variable in the view:
<!-- components/card.blade.php -->
<div class="card">
<div class="card-body">
{{ $slot }}
</div>
</div>
<x-card>
<p>This text is placed directly into the slot.</p>
</x-card>
Named slots
Most components have more than one content area: a header, a body, a footer. For this you define named slots. In the view, you mark each area with its own variable:
<!-- components/card.blade.php -->
<div class="card">
<div class="card-header">{{ $title }}</div>
<div class="card-body">{{ $slot }}</div>
</div>
When using it, you fill that area with <x-slot:title>; any unnamed content goes to the default $slot:
<x-card>
<x-slot:title>
Monthly Report
</x-slot>
<p>The main content goes here.</p>
</x-card>
Anonymous components and attributes
You don't have to write a class for every component. For simple, view-only structures, an anonymous component is enough: you just create a Blade file under resources/views/components/, no class required. You declare props at the top of the file:
<!-- components/badge.blade.php -->
@props(['color' => 'gray'])
<span class="badge badge-{{ $color }}">
{{ $slot }}
</span>
Every attribute you didn't declare is collected in the $attributes bag. You can forward these to the root element, and even merge classes intelligently:
<span {{ $attributes->merge(['class' => 'badge']) }}>
{{ $slot }}
</span>
So when you write <x-badge class="ml-2" id="role">, the existing badge class is kept and the incoming ml-2 and id are added. This approach makes components far more flexible.
Good habits in practice
- Keep components that rarely change and have simple content anonymous; choose class-based when you need logic (fetching data, computation).
- Give props clear, consistent names; define defaults to reduce misuse.
- Leave room for flexibility with
$attributes->merge(), but keep critical classes fixed. - Break up very large components; each component should do one job well.
Frequently Asked Questions
Should I use an anonymous or a class-based component?
If the content is purely a view and needs no PHP logic, an anonymous component is simpler and faster. If you need to process data, call a service or do calculations, prefer a class-based component.
What is the difference between props and slots?
Props are for passing simple values (text, numbers, booleans) from the outside and are written as attributes. A slot lets you place HTML or other components inside the component; it goes between the opening and closing tags.
Why isn't my component tag working?
The most common causes are: the file not being under resources/views/components/, a naming mismatch, or a stale view cache. Clear the cache with php artisan view:clear and compare the file path against the tag.
Want to build a clean, reusable UI layer in your project? I develop maintainable, scalable applications with Laravel and Blade. To talk through your idea, get in touch with me.