Laravel Blade Templates Explained — Layouts, Components and Real-World Patterns
Every professional Laravel application you've ever used — from SaaS dashboards to e-commerce stores — relies on Blade to keep its HTML sane. Without a templating engine, you'd copy-paste your navbar and footer into every single file. Change one link in that navbar? Now you're editing 40 files. Blade exists so that never happens to you.
Blade Layouts: The Master Template Pattern Every App Needs
The layout system is Blade's most important feature and the one most developers underuse. The idea is simple: you define one 'master' layout file that contains your HTML skeleton — doctype, head, nav, footer — and you mark certain regions as 'yield points' using @yield. Child views then 'extend' that master and fill in those yield points using @section.
This is a parent-child relationship. The parent (layout) owns the shell. The child (page view) owns only its content. The child can't accidentally break the nav because it never touches it.
There are two flavours here worth knowing: @yield with a default fallback value, and @section with @show (which renders the section immediately, useful for things like a default sidebar). Most developers only learn @yield and miss the @show trick entirely.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> {{-- @yield pulls in the child's 'title' section. --}} {{-- The second argument is the DEFAULT if the child forgets to set it. --}} <title>@yield('title', 'My App — Default Title')</title> {{-- Lets child views inject page-specific CSS without breaking global styles --}} @yield('page_styles') </head> <body> <nav class="main-nav"> <a href="/">Home</a> <a href="/products">Products</a> <a href="/contact">Contact</a> </nav> <main class="content-wrapper"> {{-- This is where every child view's content will be injected --}} @yield('content') </main> <footer> <p>© {{ date('Y') }} My App. All rights reserved.</p> </footer> {{-- Child views can push page-specific scripts here without editing this file --}} @yield('page_scripts') </body> </html>
Child Views, @section and @parent — Writing Pages That Don't Repeat Themselves
Now that the master layout exists, every page in your app just needs to extend it and fill in the blanks. The @extends directive is always the very first line of a child view — nothing can come before it, not even whitespace, or you'll get unexpected output in your HTTP headers.
The real power move here is @parent. Imagine your layout defines a default sidebar in a section. A specific page wants to keep that default sidebar AND add something extra to it. @parent lets you render the parent's version of the section and then append to it. Without @parent, you'd completely override the parent content.
This pattern becomes essential in admin panels where a global sidebar exists in the layout, but certain pages (like the settings page) need to inject extra sidebar links without nuking the global ones.
{{-- @extends must be the FIRST line. A blank line above this will cause output issues. --}}
@extends('layouts.app')
{{-- Fill the 'title' yield point we defined in the layout --}}
@section('title', 'Browse All Products')
{{-- Inject page-specific CSS. The layout's global CSS is untouched. --}}
@section('page_styles')
<link rel="stylesheet" href="/css/product-grid.css">
@endsection
{{-- This is the main payload — the unique content of this page --}}
@section('content')
<div class="page-header">
<h1>Our Products</h1>
<p>Browse {{ $products->count() }} items available today.</p>
</div>
<div class="product-grid">
{{-- @forelse is Blade's smart loop: it handles the empty-collection case gracefully --}}
@forelse($products as $product)
<div class="product-card">
<h2>{{ $product->name }}</h2>
{{-- {{ }} escapes HTML by default — protects against XSS attacks --}}
<p>{{ $product->description }}</p>
{{-- number_format keeps currency display consistent --}}
<span class="price">${{ number_format($product->price_in_cents / 100, 2) }}</span>
<a href="{{ route('products.show', $product) }}">View Details</a>
</div>
@empty
{{-- This block only renders when $products is empty — no if/else needed --}}
<div class="empty-state">
<p>No products found. Check back soon!</p>
</div>
@endforelse
</div>
@endsection
{{-- Inject the charting library only on this page — not loaded app-wide --}}
@section('page_scripts')
<script src="/js/product-filters.js"></script>
@endsection
Blade Components — Reusable UI Pieces With Real Logic Attached
Layouts handle page structure. Components handle reusable UI elements — things like alert boxes, modals, buttons, cards. A Blade component is a combination of a PHP class (for logic) and a Blade view (for markup). This separation of concerns is what makes components more powerful than simple @include.
When you run php artisan make:component AlertMessage, Laravel creates two files: app/View/Components/AlertMessage.php and resources/views/components/alert-message.blade.php. The PHP class handles data manipulation, and the view handles presentation.
Components accept data through props (typed attributes declared in the class) and content through slots. The default slot is whatever you put between the opening and closing component tags. Named slots let you inject content into specific regions of a component — just like @yield does for layouts, but scoped to that one component.
<?php namespace App\View\Components; use Illuminate\View\Component; class AlertMessage extends Component { /** * The alert type controls styling (success, warning, danger, info). * We validate it here so the VIEW stays clean and dumb. */ public string $type; public string $heading; public function __construct(string $type = 'info', string $heading = '') { // Validate the type so bad values don't slip through to the CSS class $this->type = in_array($type, ['success', 'warning', 'danger', 'info']) ? $type : 'info'; $this->heading = $heading; } /** * A computed property — available in the view as $iconClass. * Logic lives HERE, not buried in the Blade template. */ public function iconClass(): string { return match($this->type) { 'success' => 'icon-check-circle text-green-600', 'warning' => 'icon-exclamation text-yellow-600', 'danger' => 'icon-x-circle text-red-600', default => 'icon-info text-blue-600', }; } public function render() { return view('components.alert-message'); } }
Component Views, Slots and Using Components in Real Pages
The component view file is where your HTML lives. It receives all public properties from the PHP class automatically as variables. The special $slot variable holds whatever content you placed between the component tags when you used it.
Named slots let your component expose multiple content regions. Think of a card component that has a header slot, a body slot (the default $slot), and a footer slot. The page using the card decides what goes in each region — the component just provides the structure.
Anonymous components (created without a PHP class, just a Blade file in resources/views/components/) are great for purely presentational things like buttons or dividers where there's no logic involved. If it needs logic, use a class-based component. If it's just markup with props, use an anonymous component.
{{--
$type, $heading are automatically available from the PHP class public properties.
$iconClass() is called as a method — note the () syntax.
$slot holds the content placed between <x-alert-message> tags.
--}}
<div class="alert alert-{{ $type }}" role="alert">
<div class="alert-icon">
{{-- Calling the computed method from the component class --}}
<span class="{{ $iconClass() }}"></span>
</div>
<div class="alert-body">
@if($heading)
<h4 class="alert-heading">{{ $heading }}</h4>
@endif
{{-- $slot renders whatever the caller put between the component tags --}}
<p>{{ $slot }}</p>
</div>
{{-- Named slot: renders only if the caller provides a footer slot --}}
@isset($actions)
<div class="alert-actions">
{{ $actions }}
</div>
@endisset
</div>
// HOW YOU USE THIS COMPONENT in any Blade page:
// ─────────────────────────────────────────────────────────
//
// <x-alert-message type="success" heading="Order Confirmed">
// Your order #1042 has been shipped and will arrive Friday.
// <x-slot name="actions">
// <a href="/orders/1042">Track Order</a>
// </x-slot>
// </x-alert-message>
//
// ─── Browser renders: ────────────────────────────────────
// <div class="alert alert-success" role="alert">
// <div class="alert-icon"><span class="icon-check-circle text-green-600"></span></div>
// <div class="alert-body">
// <h4 class="alert-heading">Order Confirmed</h4>
// <p>Your order #1042 has been shipped and will arrive Friday.</p>
// </div>
// <div class="alert-actions"><a href="/orders/1042">Track Order</a></div>
// </div>
| Feature | @include | Blade Components |
|---|---|---|
| Purpose | Embed a static partial (nav, footer snippets) | Reusable UI element with its own logic and props |
| Passes data | Via second argument array or parent scope | Via typed props declared in PHP class constructor |
| Has logic layer | No — pure view | Yes — dedicated PHP class with testable methods |
| Slots support | No — all or nothing | Yes — default $slot + unlimited named slots |
| Unit testable | No — requires rendering | Yes — class methods are plain PHP, fully testable |
| Best used for | Simple includes like nav, footer, modals | Alert boxes, cards, buttons, data tables, form inputs |
| File location | resources/views/partials/ | resources/views/components/ + app/View/Components/ |
| Invocation syntax | @include('partials.nav') |
🎯 Key Takeaways
- The master layout + @yield pattern is the foundation of DRY HTML in Laravel — define your shell once, let every page fill in only its unique content with @section.
- @forelse is almost always better than @foreach in real apps — it handles empty collections elegantly without a separate @if($items->isEmpty()) check cluttering your view.
- Class-based Blade components separate UI logic from UI markup, making your components unit-testable without HTTP requests — push match() statements and conditionals into the PHP class, not the Blade file.
- {{ }} escapes HTML output by default and is your XSS shield — treat {!! !!} like raw SQL and only use it when you've explicitly sanitized the content yourself.
⚠ Common Mistakes to Avoid
- ✕Mistake 1: Putting whitespace or a BOM before @extends — Symptom: 'Headers already sent' error or garbled output at the top of the page because PHP has already flushed that whitespace to the browser before Laravel can set response headers. Fix: @extends must be the absolute first character in the file. No blank lines, no spaces, no HTML above it.
- ✕Mistake 2: Using {!! !!} (unescaped output) out of habit instead of {{ }} — Symptom: XSS vulnerability where a user can inject through a form field that gets stored in the database and rendered on screen. Fix: always use {{ }} by default. Only use {!! !!} for pre-sanitized HTML you trust completely, like content that has been run through an HTML purifier or generated by your own code.
- ✕Mistake 3: Stuffing conditional logic directly into Blade templates instead of component classes or view composers — Symptom: Blade files with 10+ nested @if blocks and complex PHP expressions, making them impossible to read or test. Fix: move any logic that goes beyond a simple boolean check into the component's PHP class as a computed property or method, or use a view composer to prepare data before the view renders.
Interview Questions on This Topic
- QWhat is the difference between @yield and @section/@show in Laravel Blade, and when would you use each?
- QHow do Blade components differ from @include, and what advantage does using a class-based component give you that a plain @include cannot provide?
- QIf you use {!! $userBio !!} to render a user's profile bio and a user submits '' as their bio, what happens and how do you prevent it?
Frequently Asked Questions
What is the difference between @include and Blade components in Laravel?
@include simply pulls in another Blade file and shares the current view's variable scope. Blade components have their own PHP class for logic, accept typed props, support named slots, and are fully unit-testable. Use @include for simple static partials; use components for anything that accepts data or has conditional logic.
Can I use PHP code directly inside a Blade template?
Yes, using the @php ... @endphp directive. However, you should do this sparingly. Complex logic in Blade templates makes them hard to test and maintain. Prefer pushing logic into controllers, view composers, or component classes and passing only ready-to-display data to the view.
What is the difference between {{ }} and {!! !!} in Laravel Blade?
{{ $variable }} automatically escapes HTML entities, turning