
August 12, 2026
11 min read
By Kokil Thapa | Last reviewed: September 2026
Repeated HTML across dozens of Blade views creates drift. Buttons get inconsistent classes. Form inputs miss validation states. Layout regions break when one partial changes. Blade components solve this by packaging markup, props, and optional PHP logic into reusable units. This guide walks through every pattern you need on Laravel 12 and 13—from anonymous alert boxes to class-based widgets that query the database—so your views stay maintainable as the app grows.
What Are Blade Components in Laravel and How Do They Work?
A Blade component is a self-contained view fragment you invoke with the <x-component-name /> syntax. Laravel resolves the tag to either a PHP class plus template (class-based) or a single file in resources/views/components (anonymous). Both patterns compile to the same rendering pipeline.
Under the hood, Laravel registers components during boot. When Blade encounters <x-alert type="error" />, it instantiates the backing class or anonymous view, passes declared props, collects undeclared attributes into an attribute bag, and renders the template. The result is plain HTML sent to the browser—no JavaScript framework required.
This differs from @include in three ways. Components define an explicit API through constructor parameters or @props. They receive an attribute bag for passthrough HTML attributes. They support slots for flexible content regions. On production apps I maintain, that contract alone cuts view bugs significantly compared to partials that silently depend on parent-scope variables.
If you are new to Laravel view architecture, read our modern Laravel architecture guide first. Components sit between raw Blade templates and heavier options like Livewire. They are the default choice for shared UI that does not need full request-round-trip reactivity.
How Do You Create Class-Based vs Anonymous Blade Components?
Laravel 13 supports two component types. Pick based on whether the UI needs backend logic.
Anonymous components (presentation only)
Place a file at resources/views/components/alert.blade.php. Define props with @props at the top. Invoke it as <x-alert type="success" />. No PHP class required.
<!-- resources/views/components/alert.blade.php -->
@props(['type' => 'info', 'dismissible' => false])
<div {{ $attributes->merge(['class' => 'alert alert-' . $type]) }}>
{{ $slot }}
@if ($dismissible)
<button type="button" class="btn-close" aria-label="Close"></button>
@endif
</div> Class-based components (logic and DI)
Generate with Artisan when the component needs services, computed data, or authorization checks.
php artisan make:component UserAvatar --view
<!-- app/View/Components/UserAvatar.php -->
public function __construct(
public User $user,
public int $size = 48,
) {}
public function render(): View
{
return view('components.user-avatar');
} Use class-based components when you inject repositories, call policies, or transform Eloquent models before render. Use anonymous components for buttons, badges, cards, and alerts. This split keeps your component library lean and aligns with clean Laravel architecture principles.
On legal-tech portals I have shipped, class-based components handle document status badges that check user roles. Anonymous components cover the shared alert and button library. That separation makes onboarding new developers faster because the file structure signals intent immediately.
How Does Attribute Merging Work in Blade Components?
The attribute bag is the feature that separates blade components from partials. Any HTML attribute not declared in @props or the constructor lands in $attributes. You merge, filter, and conditionally apply them without listing every possible prop.
Basic merge for defaults
<button {{ $attributes->merge([
'type' => 'button',
'class' => 'btn btn-primary',
]) }}>
{{ $slot }}
</button> Calling <x-button class="btn-lg" wire:click="save"> produces a button with merged classes plus the Livewire directive intact. The official Laravel Blade component attributes documentation covers every bag method.
Conditional classes with class()
The class() method accepts an array where keys are class names and values are booleans. This replaces messy ternary chains in templates.
<div {{ $attributes->class([
'border rounded p-4' => true,
'border-danger bg-danger-subtle' => $errors,
'opacity-50' => $disabled,
]) }}> Filtering attributes for Livewire and Alpine
When building form inputs shared between Blade and Livewire, split wire directives from styling attributes.
merge()— set safe defaults for type, class, and aria attributes.class()— toggle variant classes from boolean props.whereStartsWith('wire')— isolate Livewire bindings on the input element.except('class')— apply class logic separately from other attributes.
Pair attribute bags with Alpine.js in Blade templates for dropdowns and toggles. Pass x-data through $attributes exactly like any other HTML attribute. Our XSS prevention guide covers why you should never pass unescaped user input into attribute values.
What Are Named Slots and Scoped Slots in Laravel Blade?
The default $slot holds primary content between opening and closing component tags. Named slots let you target specific regions—headers, footers, sidebars—without splitting one component into three files.
<!-- Consumer view -->
<x-card>
<x-slot name="header">
<h2>Booking Summary</h2>
</x-slot>
<p>Your trek departs on 15 Kartik 2083.</p>
<x-slot name="footer">
<x-button wire:click="confirm">Confirm</x-button>
</x-slot>
</x-card>
<!-- components/card.blade.php -->
<div class="card">
<div class="card-header">{{ $header ?? '' }}</div>
<div class="card-body">{{ $slot }}</div>
<div class="card-footer">{{ $footer ?? '' }}</div>
</div> Scoped slots for list rendering
Scoped slots expose internal loop data to the parent. A data table component can yield each row back to the consumer for custom cell markup while keeping pagination logic encapsulated.
<!-- Inside component template -->
@foreach ($rows as $row)
{{ $row($row) }}
@endforeach
<!-- Consumer -->
<x-data-table :rows="$bookings">
@foreach ($bookings as $booking)
<x-slot :row="$booking">
<td>{{ $booking->client_name }}</td>
</x-slot>
@endforeach
</x-data-table> Component aliasing
Deep paths like admin.settings.forms.profile produce verbose tags. Register aliases in AppServiceProvider or a dedicated service provider.
use Illuminate\Support\Facades\Blade;
Blade::component('admin.settings.forms.profile', 'profile-form'); Teams building large apps often pair aliasing with domain prefixes: shop-product-card, auth-input, admin-data-table. For projects needing a full component library designed alongside backend architecture, see our custom software development services and Laravel developer guide for Nepal.
How Do You Test and Debug Blade Components in Production?
Blade components are testable units—not just template fragments. Write tests before you have three copies of the same markup drifting apart.
| Strategy | Best For | Key Method | Common Pitfall |
|---|---|---|---|
| Unit render test | Default props, computed output | Blade::renderComponent() | Unmocked constructor dependencies |
| Feature test | Auth-gated components on pages | $response->assertSee() | Testing HTML structure too tightly |
| Attribute test | Class merging, conditionals | assertStringContainsString() | Whitespace differences in output |
| Slot test | Named slots, fallback content | Render with slot closures | Missing ?? '' on optional slots |
Example component test
public function test_alert_renders_with_merged_classes(): void
{
$html = Blade::render(
'<x-alert type="danger" class="mb-3">Error occurred</x-alert>'
);
$this->assertStringContainsString('alert-danger', $html);
$this->assertStringContainsString('mb-3', $html);
$this->assertStringContainsString('Error occurred', $html);
} Follow patterns from our Laravel feature testing guide. Mock services injected into class-based components the same way you mock controller dependencies.
Debugging checklist
- Check
storage/logs/laravel.logfor TypeError on missing typed constructor props. - Run
php artisan view:clearafter renaming component files or aliases. - Verify the file path matches the tag:
components/forms/input.blade.phpmaps to<x-forms.input />. - Confirm
@propslists every attribute you expect to consume—undeclared props stay in the bag. - Test with actual Livewire bindings in staging, not just static HTML.
PHP 8.3+ typed constructor properties throw before Blade renders when a required prop is missing. Always provide defaults during development. The PHP type declarations manual explains nullable and default value syntax.
When Should You Choose Blade Components Over Partials or Livewire?
Not every UI fragment needs a component. Over-abstraction hurts readability on small projects.
Use blade components when the markup appears three or more times, when you need a stable prop contract between design and backend, or when attribute merging simplifies shared form inputs. Skip components for one-off page sections, rapidly changing marketing copy, or static content that editors manage through a CMS.
Compare the three options:
| Approach | Reactivity | Best Use Case | Overhead |
|---|---|---|---|
@include partial | None | One-off sections, simple reuse | Lowest |
| Blade component | Via Alpine/Livewire attrs | Design systems, shared UI | Low |
| Livewire component | Full server round-trip | Forms, filters, dashboards | Medium |
On high-traffic eCommerce pages, thousands of component instances in a loop can add measurable compile overhead. Benchmark hot paths with view caching strategies. Sometimes a cached include wins for product listing grids serving users across Nepal and abroad.
For a real component library in production, study the Adventure Third Pole Trek booking platform—a Laravel + Livewire app where Blade components handle static UI and Livewire owns interactive booking flows. Use our regex tester when building validation patterns inside form components.
Key Takeaways
- Start with anonymous blade components for presentation; graduate to class-based only when you need DI, queries, or authorization.
- Use
$attributes->merge()andclass()for every shared element—this is what makes components worth the migration from partials. - Define named slots with
?? ''fallbacks so optional regions never throw undefined variable errors. - Write render tests for critical components before copying markup into a fourth view.
- Run
php artisan view:clearafter renames, and test Livewire bindings in staging—not just static HTML. - Keep one-off layouts as plain Blade; componentize only when reuse or a prop contract justifies the abstraction.
People Also Ask
What is the difference between blade components and @include?
@include injects a partial with whatever variables exist in the parent scope—no explicit API, no attribute bag, no slots. Blade components define declared props, merge passthrough attributes, and support named content regions. Use includes for simple one-way insertion; use components when you need a contract and testability.
Can blade components work with Livewire and Alpine.js?
Yes. Pass wire:model, wire:click, and x-data through $attributes on the root element. Filter with whereStartsWith('wire') when splitting bindings across nested elements. Blade components render server-side; Livewire and Alpine add client interactivity on top.
How do I organize blade components in a large Laravel app?
Group by domain under resources/views/components/: forms/, admin/, shop/. Register aliases for deeply nested paths. Document prop contracts in the component template header comment. Pair with Laravel best practices for consistent naming across the codebase.
Do blade components affect Laravel performance?
Each component adds class resolution and view compilation cost. For most pages the overhead is negligible. Profile component-heavy loops on high-traffic routes. Combine with view caching and eager loading where needed. See our speed optimization services for production tuning.
Build a Component Library That Scales
Blade components turn copy-pasted HTML into a maintainable design system. Audit your partials for reuse candidates this week. Migrate alerts and buttons to anonymous components first. Add class-based components only where logic demands them. Write one render test per critical widget before the next feature sprint adds a fourth copy of the same markup.
Need help architecting a component library for a legal-tech portal, eCommerce store, or SaaS product? Contact us to discuss your Laravel project, or reach out directly if you already have a component migration in progress. Browse the Court Marriage In Nepal case study for a production Laravel UI built with reusable Blade patterns, and explore why Laravel remains the right choice in 2026 for teams shipping from Nepal and worldwide.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

