Introduction to Blade
Blade is Laravel's powerful, intuitive templating engine that combines the familiarity of PHP with convenient shortcuts and powerful features for building dynamic views.
Unlike many PHP templating engines, Blade doesn't restrict you from using plain PHP code. It compiles templates into plain PHP code which is cached until modified, meaning Blade adds essentially zero overhead to your application.
"Blade is the simple, yet powerful templating engine provided with Laravel. Unlike other popular PHP templating engines, Blade does not restrict you from using plain PHP code in your views."
Think of Blade as a Swiss Army knife for view generation - it provides specialized tools that make common view tasks easy while still allowing access to the raw power of PHP when needed.
Blade File Structure
Blade templates are stored in the resources/views directory and use the .blade.php file extension. This dual extension tells Laravel that the file contains Blade syntax that needs to be compiled before serving.
Directory Structure Example
resources/
└── views/
├── layouts/
│ ├── app.blade.php
│ └── admin.blade.php
├── partials/
│ ├── header.blade.php
│ ├── footer.blade.php
│ └── sidebar.blade.php
├── components/
│ ├── alert.blade.php
│ ├── button.blade.php
│ └── card.blade.php
├── pages/
│ ├── home.blade.php
│ ├── about.blade.php
│ └── contact.blade.php
└── auth/
├── login.blade.php
└── register.blade.php
This organization is similar to how architects organize blueprints - main structural drawings (layouts), reusable elements (components and partials), and specific room details (pages) are all logically categorized for easy reference.
Rendering Views from Controllers
Once you've created your Blade templates, you can render them from your controllers using the view() helper function:
// Basic view rendering
public function index()
{
return view('pages.home');
}
// With data
public function show($id)
{
$user = User::findOrFail($id);
return view('users.profile', ['user' => $user]);
}
// Alternative compact syntax
public function dashboard()
{
$stats = $this->statsService->gather();
$activities = Activity::recent()->get();
return view('dashboard', compact('stats', 'activities'));
}
// Using with() method for more readability
public function edit($id)
{
$post = Post::findOrFail($id);
$categories = Category::all();
return view('posts.edit')
->with('post', $post)
->with('categories', $categories);
}
The dot notation used in view names corresponds to directory structure. For example, view('users.profile') will load the file at resources/views/users/profile.blade.php.
Basic Blade Syntax
Blade provides elegant syntax for common PHP operations, making your templates cleaner and more readable:
Echo Data
// Echo a variable
{{ $name }}
// Echo with HTML escaping (default behavior)
{{ $userInput }}
// Echo without HTML escaping (use with caution!)
{!! $htmlContent !!}
// Echo with default value if variable is undefined
{{ $title ?? 'Default Title' }}
Commenting
{{-- This comment will not be present in the rendered HTML --}}
PHP Directives
// Blade if statement
@if ($user->isAdmin())
Admin
@elseif ($user->isModerator())
Moderator
@else
User
@endif
// Blade unless (opposite of if)
@unless ($user->isVerified())
Please verify your email.
@endunless
// Loops
@for ($i = 0; $i < 10; $i++)
The current value is {{ $i }}
@endfor
@foreach ($users as $user)
This is user {{ $user->name }}
@endforeach
@while ($condition)
I'm looping forever.
@endwhile
Loop Variable
Blade provides a $loop variable inside @foreach loops with useful properties:
@foreach ($users as $user)
@if ($loop->first)
This is the first iteration.
@endif
@if ($loop->last)
This is the last iteration.
@endif
{{ $loop->index }} / {{ $loop->iteration }} of {{ $loop->count }}
@if ($loop->even)
This is an even row.
@endif
@if($loop->depth > 1)
This is a nested loop.
@endif
@endforeach
Blade syntax is like shorthand notation in music - it conveys the same information as traditional notation but in a more concise and readable format, making the composition easier to interpret.
Conditional Directives
Blade provides a variety of conditional directives to handle complex logic in your templates:
Advanced Conditionals
// If statements
@if ($condition)
// content
@elseif ($anotherCondition)
// content
@else
// content
@endif
// Unless (inverse of if)
@unless ($user->subscribed())
<p>You are not subscribed.</p>
@endunless
// Isset
@isset($variable)
// Will only execute if $variable is defined and not null
@endisset
// Empty
@empty($variable)
// Will only execute if $variable is "empty"
@endempty
// Authentication directives
@auth
// The user is authenticated
@endauth
@guest
// The user is not authenticated
@endguest
// Environment directives
@production
// Only in production environment
@endproduction
@env('staging')
// Only in staging environment
@endenv
Switch Statements
@switch($grade)
@case('A')
Excellent work!
@break
@case('B')
Good job!
@break
@case('C')
Acceptable.
@break
@default
Needs improvement.
@endswitch
Blade's conditional directives are like a well-designed set of traffic signals - they efficiently direct the flow of content based on specific conditions, making your templates respond intelligently to different scenarios.
Template Inheritance
One of Blade's most powerful features is template inheritance, which allows you to build a base layout that can be extended by child views. This promotes DRY (Don't Repeat Yourself) principles and maintainable code.
Parent Layout (layouts/app.blade.php)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@yield('title', 'Default Title') - My Site</title>
<link rel="stylesheet" href="/css/app.css">
@yield('styles')
</head>
<body>
<header>
@include('partials.navigation')
</header>
<main class="container">
@yield('content')
</main>
<footer>
@include('partials.footer')
</footer>
<script src="/js/app.js"></script>
@yield('scripts')
</body>
</html>
Child View (pages/home.blade.php)
@extends('layouts.app')
@section('title', 'Home Page')
@section('styles')
<link rel="stylesheet" href="/css/home.css">
@endsection
@section('content')
<h1>Welcome to Our Website</h1>
<p>This is the home page content.</p>
<div class="featured-products">
@foreach($products as $product)
@include('partials.product-card', ['product' => $product])
@endforeach
</div>
@endsection
@section('scripts')
<script src="/js/home.js"></script>
@endsection
Partial (partials/product-card.blade.php)
<div class="product-card">
<img src="{{ $product->image }}" alt="{{ $product->name }}">
<h3>{{ $product->name }}</h3>
<p class="price">${{ $product->price }}</p>
<button type="button">Add to Cart</button>
</div>
Template inheritance in Blade is like modular furniture design - you create a base structure (layout) and then customize specific sections (content, title, styles) for each room (page) while maintaining consistent overall design.
Understanding @yield, @section, and @include
- @yield('name', 'default') - Defines a section in the parent template that will be replaced by content from child views. Can include an optional default value.
- @section/@endsection - Defines content in child views that will replace the corresponding @yield in the parent template.
- @include('view.name') - Embeds another view (partial) within the current template, useful for reusable components.
- @includeIf('view.name', ['data' => 'value']) - Includes a view only if it exists, passing optional data.
- @includeWhen($condition, 'view.name') - Includes a view only if the condition is true.
Components & Slots
Blade components are a more powerful way to create reusable template parts with clear interfaces. They're similar to components in modern JavaScript frameworks.
Anonymous Components
Anonymous components are simple Blade templates stored in resources/views/components directory:
Component Definition (resources/views/components/alert.blade.php)
<div class="alert alert-{{ $type ?? 'info' }}">
<div class="alert-title">{{ $title }}</div>
{{ $slot }}
@if(isset($footer))
<div class="alert-footer">
{{ $footer }}
</div>
@endif
</div>
Using the Component
<x-alert type="danger" title="Error">
<p>Something went wrong! Please try again.</p>
<x-slot name="footer">
<a href="#" class="btn">Dismiss</a>
</x-slot>
</x-alert>
Class-Based Components
For more complex components with logic, you can use class-based components:
Generate a Class Component
php artisan make:component Alert
Component Class (app/View/Components/Alert.php)
namespace App\View\Components;
use Illuminate\View\Component;
class Alert extends Component
{
public $type;
public $title;
public function __construct($type = 'info', $title = null)
{
$this->type = $type;
$this->title = $title;
}
public function render()
{
return view('components.alert');
}
// Helper method available in the view
public function isImportant()
{
return in_array($this->type, ['danger', 'warning']);
}
}
Component View (resources/views/components/alert.blade.php)
<div class="alert alert-{{ $type }}">
@if($title)
<div class="alert-title {{ $isImportant() ? 'font-bold' : '' }}">
{{ $title }}
</div>
@endif
<div class="alert-content">
{{ $slot }}
</div>
</div>
Using the Component
<x-alert type="danger" title="Server Error">
The server encountered an unexpected condition.
</x-alert>
Blade components are like prefabricated building sections in construction - they are designed once, manufactured with precision, and can be assembled quickly in various combinations to create different structures while maintaining consistent quality.
Forms, CSRF, & Method Fields
Blade makes it easy to work with forms, automatically handling CSRF tokens and method spoofing for HTTP verbs not supported by HTML forms.
Basic Form with CSRF Protection
<form action="/posts" method="POST">
@csrf
<div class="form-group">
<label for="title">Title</label>
<input type="text" name="title" id="title" value="{{ old('title') }}">
</div>
<div class="form-group">
<label for="content">Content</label>
<textarea name="content" id="content">{{ old('content') }}</textarea>
</div>
<button type="submit">Create Post</button>
</form>
PUT, PATCH, DELETE Method Spoofing
<form action="/posts/{{ $post->id }}" method="POST">
@csrf
@method('PUT')
<!-- Form fields -->
<button type="submit">Update Post</button>
</form>
<form action="/posts/{{ $post->id }}" method="POST">
@csrf
@method('DELETE')
<button type="submit" class="btn-danger">Delete Post</button>
</form>
Form Validation Errors
<form action="/posts" method="POST">
@csrf
<div class="form-group">
<label for="title">Title</label>
<input type="text" name="title" id="title" value="{{ old('title') }}"
class="@error('title') is-invalid @enderror">
@error('title')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<!-- More form fields -->
<button type="submit">Create Post</button>
</form>
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
Blade's form handling features are like built-in safety features of modern vehicles - they automatically protect against common vulnerabilities (CSRF) and provide mechanisms to work around limitations (method spoofing) without requiring the developer to implement these protections manually.
Directives for Assets & URLs
Blade provides several directives to work with assets, URLs, and routes:
Asset Helpers
<!-- Generate URL for an asset in the public directory -->
<img src="{{ asset('images/logo.png') }}">
<!-- Generate URL for the current secure asset (HTTPS) -->
<link rel="stylesheet" href="{{ secure_asset('css/app.css') }}">
<!-- Generate a URL for a named route -->
<a href="{{ route('posts.show', $post) }}">View Post</a>
<!-- Generate a URL for a controller action -->
<a href="{{ action([PostController::class, 'index']) }}">All Posts</a>
<!-- URL helper -->
<a href="{{ url('/posts/' . $post->id) }}">View Post</a>
These helpers ensure your URLs are always correct, even if your application is running in a subdirectory or if you change route definitions.
Custom Blade Directives
You can extend Blade with your own custom directives to encapsulate complex logic or formatting:
Creating Custom Directives
// In AppServiceProvider.php
public function boot()
{
// Simple directive
Blade::directive('datetime', function ($expression) {
return "format('Y-m-d H:i'); ?>";
});
// More complex directive
Blade::directive('role', function ($expression) {
return "check() && auth()->user()->hasRole($expression)): ?>";
});
Blade::directive('endrole', function () {
return "";
});
// Directive with parameters
Blade::directive('currency', function ($expression) {
return "";
});
}
Using Custom Directives
<!-- Format a date -->
Created: @datetime($post->created_at)
<!-- Check user role -->
@role('admin')
<a href="{{ route('admin.dashboard') }}">Admin Dashboard</a>
@endrole
<!-- Format currency -->
Price: @currency($product->price)
Custom Blade directives are like creating your own specialized tools for specific tasks - they encapsulate complex operations into simple, readable syntax that makes your templates cleaner and more expressive.
Blade and JavaScript Frameworks
When working with JavaScript frameworks like Vue.js or React, you might encounter conflicts with the curly brace syntax. Blade provides ways to handle this:
Working with JavaScript Frameworks
// Using the @verbatim directive to ignore Blade syntax
@verbatim
<div id="app">
{{ vueData }}
</div>
@endverbatim
// Using the @ symbol to escape Blade syntax
<script>
let app = new Vue({
data: {
message: '@{{ This will not be processed by Blade }}'
}
});
</script>
Blade's JavaScript compatibility features are like diplomatic translators who ensure different languages (template engines) can coexist peacefully without misinterpretations.
Blade Stacks
Blade stacks allow you to specify a named location where content can be pushed from child views, which is especially useful for scripts and styles:
Defining a Stack in Layout
<!-- In layouts/app.blade.php -->
<head>
<!-- ... -->
@stack('styles')
</head>
<body>
<!-- ... -->
@stack('scripts')
</body>
Pushing to a Stack from Child Views
@push('styles')
<link rel="stylesheet" href="/css/chart.css">
@endpush
@push('scripts')
<script src="/js/chart.js"></script>
<script>
// Initialize chart
const chart = new Chart(/*...*/);
</script>
@endpush
@prepend('scripts')
<!-- This will be added at the beginning of the scripts stack -->
<script src="/js/chart-dependencies.js"></script>
@endprepend
Blade stacks are like assembly points in manufacturing where different components arrive from various stations to be combined in a specific order - they provide a structured way to gather and organize content from different template parts.
Advanced Blade Features
Blade includes several advanced features that can make your templates even more powerful:
Service Injection
Inject services directly into your views using the @inject directive:
@inject('metrics', 'App\Services\MetricsService')
<div>Monthly Revenue: {{ $metrics->monthlyRevenue() }}</div>
PHP in Blade
Execute raw PHP code with the @php directive:
@php
$counter = 0;
function increment() {
global $counter;
return ++$counter;
}
@endphp
<p>Counter: {{ increment() }}</p>
<p>Counter: {{ increment() }}</p>
Once Directive
Include content only once per rendering cycle:
@once
@push('scripts')
<script src="/js/chart-lib.js"></script>
@endpush
@endonce
Each Directive
A shorthand for foreach with empty fallback:
@each('partials.comment', $comments, 'comment', 'partials.no-comments')
This renders partials.comment for each comment, or partials.no-comments if the array is empty.
Custom If Statements
Register custom conditional directives:
// In AppServiceProvider
Blade::if('subscribed', function ($plan = null) {
return auth()->check() &&
(is_null($plan) || auth()->user()->subscribedTo($plan));
});
// In the template
@subscribed('premium')
<div>Premium content</div>
@else
<div>Subscribe to premium to access this content.</div>
@endsubscribed
These advanced features are like specialized functions on a professional camera - while not needed for everyday use, they provide powerful capabilities for specific scenarios when you need them.
Practical Example: Building a Blog Template
Let's put all these concepts together with a comprehensive example of a blog template system:
Main Layout (layouts/app.blade.php)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@yield('title', 'My Blog') | BlogEngine</title>
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
@stack('styles')
</head>
<body>
<header class="site-header">
@include('partials.navigation')
</header>
<main class="container">
@hasSection('header')
<div class="page-header">
@yield('header')
</div>
@endif
@if(session('status'))
<x-alert type="success">
{{ session('status') }}
</x-alert>
@endif
@yield('content')
</main>
<footer class="site-footer">
@include('partials.footer')
</footer>
<script src="{{ asset('js/app.js') }}"></script>
@stack('scripts')
</body>
</html>
Blog Post Index (posts/index.blade.php)
@extends('layouts.app')
@section('title', 'Blog Posts')
@section('header')
<h1>Our Blog</h1>
<p>Latest thoughts, ideas, and stories</p>
@auth
<a href="{{ route('posts.create') }}" class="btn btn-primary">New Post</a>
@endauth
@endsection
@section('content')
<div class="post-filters">
<form action="{{ route('posts.index') }}" method="GET">
<select name="category" onchange="this.form.submit()">
<option value="">All Categories</option>
@foreach($categories as $category)
<option value="{{ $category->id }}"
{{ request('category') == $category->id ? 'selected' : '' }}>
{{ $category->name }}
</option>
@endforeach
</select>
</form>
</div>
<div class="post-grid">
@forelse($posts as $post)
<x-post-card :post="$post" />
@empty
<div class="empty-state">
<p>No posts found.</p>
@if(request('category') || request('search'))
<p>Try adjusting your filters.</p>
@endif
</div>
@endforelse
</div>
{{ $posts->links() }}
@endsection
Post Card Component (components/post-card.blade.php)
<article class="post-card">
@if($post->featured_image)
<div class="post-image">
<img src="{{ asset('storage/' . $post->featured_image) }}"
alt="{{ $post->title }}">
</div>
@endif
<div class="post-meta">
<span class="post-category">{{ $post->category->name }}</span>
<span class="post-date">{{ $post->created_at->format('M d, Y') }}</span>
</div>
<h2 class="post-title">
<a href="{{ route('posts.show', $post) }}">
{{ $post->title }}
</a>
</h2>
<div class="post-excerpt">
{{ Str::limit($post->excerpt ?? $post->body, 150) }}
</div>
<footer class="post-footer">
<div class="post-author">
By {{ $post->author->name }}
</div>
<a href="{{ route('posts.show', $post) }}" class="read-more">
Read More →
</a>
</footer>
</article>
Single Post View (posts/show.blade.php)
@extends('layouts.app')
@section('title', $post->title)
@push('meta')
<meta name="description" content="{{ $post->excerpt }}">
<meta property="og:title" content="{{ $post->title }}">
<meta property="og:description" content="{{ $post->excerpt }}">
@if($post->featured_image)
<meta property="og:image" content="{{ asset('storage/' . $post->featured_image) }}">
@endif
@endpush
@section('content')
<article class="post-single">
<header class="post-header">
<div class="post-meta">
<a href="{{ route('categories.show', $post->category) }}" class="post-category">
{{ $post->category->name }}
</a>
<span class="post-date">{{ $post->created_at->format('F j, Y') }}</span>
</div>
<h1 class="post-title">{{ $post->title }}</h1>
<div class="post-author">
<img src="{{ $post->author->avatar }}" alt="{{ $post->author->name }}" class="author-avatar">
By {{ $post->author->name }}
</div>
</header>
@if($post->featured_image)
<div class="post-featured-image">
<img src="{{ asset('storage/' . $post->featured_image) }}"
alt="{{ $post->title }}">
</div>
@endif
<div class="post-content">
{!! $post->body !!}
</div>
<div class="post-tags">
@foreach($post->tags as $tag)
<a href="{{ route('tags.show', $tag) }}" class="tag">
#{{ $tag->name }}
</a>
@endforeach
</div>
<div class="post-share">
<h3>Share this post</h3>
<div class="share-buttons">
@include('partials.share-buttons', ['url' => route('posts.show', $post), 'title' => $post->title])
</div>
</div>
@auth
@if(auth()->user()->can('update', $post))
<div class="post-actions">
<a href="{{ route('posts.edit', $post) }}" class="btn btn-secondary">Edit Post</a>
<form action="{{ route('posts.destroy', $post) }}" method="POST" class="d-inline">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-danger"
onclick="return confirm('Are you sure you want to delete this post?')">
Delete Post
</button>
</form>
</div>
@endif
@endauth
<section class="post-comments">
<h2>Comments ({{ $post->comments_count }})</h2>
@auth
@include('partials.comment-form', ['post' => $post])
@else
<p><a href="{{ route('login') }}">Log in</a> to leave a comment.</p>
@endauth
@if($post->comments_count > 0)
<div class="comments-list">
@foreach($post->comments as $comment)
<x-comment :comment="$comment" />
@endforeach
</div>
@else
<p>No comments yet. Be the first to comment!</p>
@endif
</section>
@if(count($relatedPosts) > 0)
<section class="related-posts">
<h2>Related Posts</h2>
<div class="post-grid">
@foreach($relatedPosts as $relatedPost)
<x-post-card :post="$relatedPost" />
@endforeach
</div>
</section>
@endif
</article>
@endsection
@push('scripts')
<script src="{{ asset('js/syntax-highlighter.js') }}"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
SyntaxHighlighter.highlight();
});
</script>
@endpush
This comprehensive example demonstrates how the various Blade features come together to create a complete, maintainable template structure for a blog application.
Best Practices
Following these best practices will help you create maintainable, efficient Blade templates:
Template Organization
- Use a consistent directory structure (layouts, partials, components, pages)
- Keep templates focused on presentation logic, not business logic
- Break complex templates into smaller, reusable partials
- Use components for UI elements that appear in multiple places
Performance Considerations
- Remember Blade templates are cached - you don't need to worry about directive overhead
- Use @foreach instead of @for when iterating through collections
- Use eager loading to prevent N+1 query problems when displaying relationships
- Consider using pagination for large data sets
Security Best Practices
- Always use {{ }} double curly braces which automatically escape HTML
- Only use {!! !!} unescaped output for trusted content, never for user input
- Always include @csrf in forms to prevent cross-site request forgery
- Use policy checks in templates (@can/@cannot) to control access to actions
Maintainability Tips
- Use consistent naming conventions for template files and sections
- Add comments for complex sections using {{-- --}} syntax
- Extract repeated logic into custom directives or components
- Keep templates DRY (Don't Repeat Yourself) through inheritance and includes
Following these best practices is like following architectural standards when building a house - they ensure your template structure is solid, efficient, secure, and maintainable over time.
Practical Activity: Building a Dashboard Template
Let's practice what we've learned with a hands-on activity:
Activity: Create a Dashboard Template System
-
Set up the base layout file:
- Create
layouts/dashboard.blade.phpwith header, sidebar, main content area, and footer - Include yield directives for title, content, and any additional scripts/styles
- Create
-
Create reusable components:
- Create a stat card component (
components/stat-card.blade.php) that displays a metric with icon, title, and value - Create a data table component (
components/data-table.blade.php) that displays tabular data with sorting and filtering
- Create a stat card component (
-
Create dashboard pages:
dashboard/index.blade.php- Overview with multiple stat cards and recent activitydashboard/users.blade.php- User management screen with data table
-
Implement conditional display:
- Show/hide sidebar menu items based on user permissions
- Display different content for different user roles
Extension: Add a dark/light theme toggle that persists user preference using session or local storage.
Laravel Blade vs Other Templating Engines
It's useful to understand how Blade compares to other popular templating engines:
| Feature | Laravel Blade | Twig (Symfony) | Smarty | Handlebars.js |
|---|---|---|---|---|
| Syntax | @directives and {{ }} for output | {% %} for logic, {{ }} for output | {php} blocks and {$variable} | {{#helpers}} and {{variable}} |
| PHP Integration | Full PHP support | Restricted, sandboxed | Partial support | No direct PHP |
| Inheritance | @extends/@section | extends/block | extends/block | Partial support via partials |
| Components | Rich component system | Macros and includes | Function plugins | Partials and helpers |
| Performance | Compiled and cached | Compiled and cached | Compiled and cached | Precompiled client-side |
| Learning Curve | Low to moderate | Moderate | Moderate | Low |
| Best For | Laravel apps with mixed PHP/HTML | Symfony apps, strict separation | Legacy PHP applications | JavaScript-centric apps |
The choice of templating engine is like choosing a language for communication - each has its strengths and is optimized for specific contexts and development philosophies.
Summary and Key Takeaways
- Blade is Laravel's powerful templating engine that combines clean syntax with PHP's flexibility
- Template inheritance with @extends/@yield/@section provides a powerful way to share layouts
- Components and slots create reusable UI elements with clear interfaces
- Directives like @if, @foreach, and @switch provide convenient control structures
- Forms are simplified with @csrf and @method directives for security and HTTP verb handling
- Stacks allow pushing content from child views to specific locations in layouts
- Custom directives can extend Blade with application-specific functionality
- Blade templates are compiled to PHP and cached for optimal performance
Armed with these Blade templating skills, you're now equipped to create elegant, maintainable views for your Laravel applications.
Further Resources
- Laravel Blade Documentation
- Laracasts: Blade Component Examples
- Blade UI Kit - Collection of Blade Components
- "Laravel: Up & Running" by Matt Stauffer (Chapter on Blade)
- "Mastering Laravel" by Christopher John Pecoraro