Blade components let you extract a reusable piece of markup — a button, a card, an alert box — into its own file, called like an HTML tag, with data passed in as attributes.
Creating a component
php artisan make:component Alert
This generates two files: app/View/Components/Alert.php (the class, if class-backed) and resources/views/components/alert.blade.php (the markup).
Passing data in
// app/View/Components/Alert.php
class Alert extends Component
{
public function __construct(public string $type = 'info') {}
public function render(): View
{
return view('components.alert');
}
}
{{-- resources/views/components/alert.blade.php --}}
<div class="alert alert-{{ $type }}">
{{ $slot }}
</div>
Using it
<x-alert type="success">
Your changes have been saved.
</x-alert>
$slot captures whatever content is placed between the opening and closing tags — the same pattern as a native HTML element's children.
Anonymous components — no class needed
For simpler components that don't need constructor logic, a Blade file alone (no PHP class) works, using @props to declare accepted attributes with defaults:
{{-- resources/views/components/badge.blade.php --}}
@props(['color' => 'gray'])
<span class="badge badge-{{ $color }}">{{ $slot }}</span>
Anonymous components are the better default for markup-only reuse — reach for a class-backed component specifically when you need actual PHP logic (computed values, method calls) behind the component, not just prop pass-through.
Named slots for multi-part components
<x-card>
<x-slot:header>Order #{{ $order->id }}</x-slot:header>
Order details go here.
</x-card>
Named slots let a component accept multiple distinct content areas (a header and a body, for example), not just one undifferentiated block of slot content.
Forward ordinary HTML attributes with the attribute bag
A reusable component becomes much more useful when callers can add classes, IDs, ARIA attributes, and data attributes without the component declaring every possible prop. Blade exposes those through $attributes:
@props(['type' => 'info'])
<div {{ $attributes->class(['alert', 'alert-'.$type]) }}>
{{ $slot }}
</div>
<x-alert type="success" class="mb-4" role="status">
Saved.
</x-alert>
The component owns its required base classes while the caller can still extend the markup. That is usually cleaner than adding a $class prop and manually concatenating strings.
Props and attributes are not the same thing
Values declared by @props are pulled out for component logic; remaining attributes stay in the attribute bag. That separation lets type="success" influence the template without accidentally rendering a nonstandard type attribute on a div, while aria-live or data-controller can pass through untouched.
Keep database queries out of presentational components
A class-backed component technically can query the database, but that can hide expensive work inside a piece of markup rendered many times in a loop. Pass already-loaded data into the component instead. If a component needs substantial application state, treat it as a deliberate view-model boundary and make the cost obvious rather than turning a badge or card into an invisible query source.
Components are most valuable when they enforce a repeated contract
Extracting three lines of markup used once does not buy much. A component pays off when several views need the same accessibility attributes, states, slots, class conventions, or error behavior; changing that contract in one file is the real benefit, not simply reducing the number of HTML lines in a Blade template.