Introduction to Blade
Blade is Laravel's powerful templating engine that makes writing views enjoyable and productive. Unlike other PHP templating engines, Blade does not restrict you from using plain PHP code in your views.
Why Blade? Blade provides convenient shortcuts for common PHP operations, template inheritance, component-based architecture, and compiles to plain PHP for optimal performance. All Blade views are cached until modified!
Blade File Location and Naming
Blade templates are stored in resources/views and use the .blade.php file extension:
resources/views/
├── layouts/
│ └── app.blade.php
├── posts/
│ ├── index.blade.php
│ ├── show.blade.php
│ ├── create.blade.php
│ └── edit.blade.php
├── home.blade.php
└── welcome.blade.php
Displaying Data
Echo Statements
Display data passed to your views using double curly braces:
<!-- Blade syntax -->
<h1>{{ $title }}</h1>
<p>Welcome, {{ $name }}!</p>
<!-- Compiles to: -->
<h1><?php echo htmlspecialchars($title, ENT_QUOTES, 'UTF-8'); ?></h1>
<p>Welcome, <?php echo htmlspecialchars($name, ENT_QUOTES, 'UTF-8'); ?>!</p>
Auto-Escaping: Blade's {{ }} statements automatically escape HTML to prevent XSS attacks. The data is passed through PHP's htmlspecialchars function.
Displaying Unescaped Data
When you need to display raw HTML (use cautiously!):
<!-- Raw HTML output (not escaped) -->
<div>{!! $htmlContent !!}</div>
<!-- Example: displaying rich text from database -->
<article>
<h1>{{ $post->title }}</h1>
<div>{!! $post->content !!}</div>
</article>
Security Warning: Only use {!! !!} with trusted data! Never use it with user-submitted content without proper sanitization, as it can lead to XSS vulnerabilities.
Rendering JSON
<script>
// Blade automatically encodes to JSON
var app = @json($array);
// With options
var app = @json($array, JSON_PRETTY_PRINT);
</script>
Default Values
<!-- Display $name or 'Guest' if $name is undefined -->
<p>Hello, {{ $name ?? 'Guest' }}</p>
<!-- Using ternary operator -->
<p>Status: {{ $isActive ? 'Active' : 'Inactive' }}</p>
Blade Directives
Conditional Statements
<!-- @if, @elseif, @else, @endif -->
@if ($user->isAdmin())
<p>Welcome, Administrator!</p>
@elseif ($user->isModerator())
<p>Welcome, Moderator!</p>
@else
<p>Welcome, User!</p>
@endif
<!-- @unless (opposite of @if) -->
@unless ($user->isPremium())
<div class="upgrade-banner">
Upgrade to Premium!
</div>
@endunless
<!-- @isset and @empty -->
@isset($records)
<p>Records are available</p>
@endisset
@empty($records)
<p>No records found</p>
@endempty
Authentication Directives
<!-- Check if user is authenticated -->
@auth
<p>You are logged in!</p>
@endauth
<!-- Check if user is guest -->
@guest
<a href="/login">Please login</a>
@endguest
<!-- Combination -->
@auth
<a href="/dashboard">Dashboard</a>
@else
<a href="/login">Login</a>
@endauth
Switch Statements
@switch($role)
@case('admin')
<p>Administrator Access</p>
@break
@case('editor')
<p>Editor Access</p>
@break
@case('viewer')
<p>View-Only Access</p>
@break
@default
<p>Guest Access</p>
@endswitch
Loops
<!-- @for loop -->
@for ($i = 0; $i < 10; $i++)
<p>Item {{ $i }}</p>
@endfor
<!-- @foreach loop -->
@foreach ($posts as $post)
<article>
<h2>{{ $post->title }}</h2>
<p>{{ $post->excerpt }}</p>
</article>
@endforeach
<!-- @forelse (with empty state) -->
@forelse ($posts as $post)
<article>
<h2>{{ $post->title }}</h2>
</article>
@empty
<p>No posts available.</p>
@endforelse
<!-- @while loop -->
@while ($condition)
<p>Still looping...</p>
@endwhile
Loop Variable
Inside loops, Blade provides a $loop variable with useful information:
@foreach ($posts as $post)
<div class="post
{{ $loop->first ? 'first' : '' }}
{{ $loop->last ? 'last' : '' }}
{{ $loop->even ? 'even' : 'odd' }}">
<h3>{{ $post->title }}</h3>
<!-- Loop information -->
<small>
Post {{ $loop->iteration }} of {{ $loop->count }}
(Index: {{ $loop->index }}, Remaining: {{ $loop->remaining }})
</small>
</div>
@endforeach
Available $loop properties:
$loop->index - Current iteration index (starts at 0)
$loop->iteration - Current iteration number (starts at 1)
$loop->remaining - Remaining iterations
$loop->count - Total items in array
$loop->first - Boolean: first iteration
$loop->last - Boolean: last iteration
$loop->even - Boolean: even iteration
$loop->odd - Boolean: odd iteration
$loop->depth - Nesting level of loop
$loop->parent - Parent loop variable in nested loops
Template Inheritance
Defining a Layout
Create a master layout that other views can extend:
<!-- resources/views/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 Application')</title>
<!-- Styles -->
<link rel="stylesheet" href="/css/app.css">
@stack('styles')
</head>
<body>
<header>
@include('partials.navigation')
</header>
<main>
@yield('content')
</main>
<footer>
@include('partials.footer')
</footer>
<!-- Scripts -->
<script src="/js/app.js"></script>
@stack('scripts')
</body>
</html>
Extending a Layout
<!-- resources/views/posts/index.blade.php -->
@extends('layouts.app')
@section('title', 'All Posts')
@section('content')
<h1>Blog Posts</h1>
@foreach ($posts as $post)
<article>
<h2>{{ $post->title }}</h2>
<p>{{ $post->excerpt }}</p>
<a href="{{ route('posts.show', $post) }}">Read More</a>
</article>
@endforeach
@endsection
@push('scripts')
<script src="/js/posts.js"></script>
@endpush
@yield vs @section
<!-- @yield: Simple content replacement -->
@yield('title')
@yield('content')
<!-- @section with @show: Define default content that can be overridden -->
@section('sidebar')
<!-- Default sidebar content -->
<p>Default sidebar</p>
@show
<!-- Child can extend parent section -->
@section('sidebar')
@parent <!-- Include parent content -->
<p>Additional sidebar content</p>
@endsection
Including Sub-Views
Basic Include
<!-- Include a partial view -->
@include('partials.alert')
<!-- Include with data -->
@include('partials.alert', ['type' => 'success', 'message' => 'Saved!'])
<!-- Include only if view exists -->
@includeIf('partials.sidebar')
<!-- Include when condition is true -->
@includeWhen($user->isAdmin(), 'partials.admin-panel')
<!-- Include unless condition is true -->
@includeUnless($user->isGuest(), 'partials.user-menu')
<!-- Include first existing view -->
@includeFirst(['custom.header', 'partials.header'])
Creating a Partial
<!-- resources/views/partials/alert.blade.php -->
@if (session('success'))
<div class="alert alert-success">
{{ session('success') }}
</div>
@endif
@if (session('error'))
<div class="alert alert-error">
{{ session('error') }}
</div>
@endif
Blade Components
Creating Anonymous Components
<!-- resources/views/components/alert.blade.php -->
@props(['type' => 'info', 'dismissible' => false])
<div {{ $attributes->merge(['class' => "alert alert-$type"]) }}>
@if ($dismissible)
<button type="button" class="close">×</button>
@endif
{{ $slot }}
</div>
Using Components
<!-- Simple usage -->
<x-alert>
This is an info alert!
</x-alert>
<!-- With attributes -->
<x-alert type="success" dismissible="true">
Record saved successfully!
</x-alert>
<!-- Passing additional classes -->
<x-alert type="error" class="mt-4">
An error occurred!
</x-alert>
Named Slots
<!-- Component definition -->
<!-- resources/views/components/card.blade.php -->
<div class="card">
<div class="card-header">
{{ $header }}
</div>
<div class="card-body">
{{ $slot }}
</div>
@isset($footer)
<div class="card-footer">
{{ $footer }}
</div>
@endisset
</div>
<!-- Using the component -->
<x-card>
<x-slot:header>
Card Title
</x-slot>
<!-- Default slot (card body) -->
This is the card content.
<x-slot:footer>
<button>Save</button>
</x-slot>
</x-card>
Class-Based Components
# Generate class-based component
php artisan make:component Button
# Creates:
# - app/View/Components/Button.php
# - resources/views/components/button.blade.php
<?php
// app/View/Components/Button.php
namespace App\View\Components;
use Illuminate\View\Component;
class Button extends Component
{
public $type;
public $size;
public function __construct($type = 'primary', $size = 'medium')
{
$this->type = $type;
$this->size = $size;
}
public function render()
{
return view('components.button');
}
}
<!-- resources/views/components/button.blade.php -->
<button {{ $attributes->merge(['class' => "btn btn-$type btn-$size"]) }}>
{{ $slot }}
</button>
<!-- Usage -->
<x-button type="success" size="large" id="save-btn">
Save Changes
</x-button>
Useful Blade Directives
CSRF Protection
<form method="POST" action="/posts">
@csrf
<!-- Generates hidden CSRF token field -->
<input type="text" name="title">
<button type="submit">Submit</button>
</form>
Method Spoofing
<!-- Browsers only support GET and POST -->
<!-- Use @method to simulate PUT, PATCH, DELETE -->
<form method="POST" action="/posts/1">
@csrf
@method('PUT')
<!-- Generates hidden _method field with value PUT -->
<input type="text" name="title">
<button type="submit">Update</button>
</form>
<form method="POST" action="/posts/1">
@csrf
@method('DELETE')
<button type="submit">Delete</button>
</form>
Session Data
<!-- Display session message -->
@if (session('status'))
<div class="alert">
{{ session('status') }}
</div>
@endif
<!-- Check if session has key -->
@session('status')
<div class="alert">
{{ $value }}
</div>
@endsession
Old Input
<!-- Repopulate form with old input after validation errors -->
<input type="text" name="title" value="{{ old('title') }}">
<input type="email" name="email" value="{{ old('email', $user->email) }}">
Validation Errors
<!-- Display all errors -->
@if ($errors->any())
<div class="alert alert-error">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<!-- Display error for specific field -->
<input type="text" name="email">
@error('email')
<span class="error">{{ $message }}</span>
@enderror
Raw PHP
<!-- Execute raw PHP code -->
@php
$counter = 0;
$isActive = true;
@endphp
<!-- Use the variables -->
<p>Counter: {{ $counter }}</p>
Comments
<!-- HTML comment (visible in source) -->
<!-- This is an HTML comment -->
{{-- Blade comment (not rendered in HTML) --}}
{{-- This comment won't appear in the compiled view --}}
Custom Directives
You can create custom Blade directives in a service provider:
// app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Blade;
public function boot()
{
// Create @datetime directive
Blade::directive('datetime', function ($expression) {
return "<?php echo ($expression)->format('m/d/Y H:i'); ?>";
});
// Create @money directive
Blade::directive('money', function ($expression) {
return "<?php echo '$' . number_format($expression, 2); ?>";
});
}
// Usage in views:
// @datetime($post->created_at)
// @money($product->price)
Practice Exercise 1: Master Layout
Create a complete layout structure:
- Create
resources/views/layouts/app.blade.php with header, nav, main content area, sidebar, and footer
- Create partial views for navigation, sidebar, and footer in
resources/views/partials/
- Use @yield for title and content sections
- Add @stack for styles and scripts
- Create a child view that extends this layout
Practice Exercise 2: Blog Post List
Create a blog posts listing page:
- Pass an array of posts from controller to view
- Use @forelse to loop through posts
- Display "No posts found" message when empty
- Add @if statements to show "New!" badge for posts created in last 7 days
- Use $loop variable to add "featured" class to first post
- Display alternating background colors using $loop->even
Practice Exercise 3: Alert Component
Create a reusable alert component:
- Create
resources/views/components/alert.blade.php
- Accept props: type (success/error/warning/info), dismissible (boolean), title (optional)
- Use @props and $attributes->merge() for dynamic classes
- Add named slots for title and actions (buttons)
- Use the component in different views with different variants
Summary
In this lesson, you've mastered:
- Displaying data with Blade's echo statements and escaping
- Using Blade directives for conditionals, loops, and authentication
- Template inheritance with @extends, @section, and @yield
- Including partial views with @include and its variants
- Creating and using Blade components (anonymous and class-based)
- Working with named slots and component attributes
- Essential directives: @csrf, @method, @error, @old
- Creating custom Blade directives
Blade is a powerful, elegant templating engine that makes building dynamic views a pleasure. You now have the foundation to create beautiful, maintainable view templates in Laravel!
In the next lessons, we'll explore databases with Eloquent ORM, migrations, and relationships to build fully-featured applications.