๐ Blade Template Engine
Blade is Laravel's templating engine โ the layer that turns your data into HTML. It gives you concise, readable syntax for the everyday jobs of a view (printing data, looping, branching) while compiling down to plain, cached PHP so it adds essentially zero runtime overhead.
๐ฏ Learning Objectives
By the end of this lesson, you will be able to:
- Echo data safely with
{{ }}and know when unescaped output is dangerous - Use Blade control directives (
@if,@foreach,@auth, the$loopvariable) - Build maintainable pages with template inheritance and layouts
- Create reusable UI with components and slots
- Write secure forms using
@csrf,@method, and@error, and organise assets with stacks
Estimated Time: 40โ50 minutes โข Difficulty: Intermediate
Hands-on: Build a small layout, a reusable component, and a page that extends the layout.
In This Lesson
What Is Blade?
Blade is the templating engine bundled with Laravel. Templates live in resources/views and use the .blade.php extension, which tells Laravel to compile them before serving. The compiled output is cached plain PHP, so after the first render there is no parsing cost โ Blade is fast and convenient.
Unlike some engines, Blade never locks you out of plain PHP; it simply gives you shorter, clearer syntax for the common cases. Think of it as a Swiss Army knife for views: purpose-built tools for the frequent jobs, with the raw blade of PHP still available when you truly need it.
๐ก Rendering a view: Controllers return views with theview()helper. View names use dot notation for folders โview('users.profile')loadsresources/views/users/profile.blade.php.
<?php
// A few ways to pass data into a view
return view('pages.home');
return view('users.profile', ['user' => $user]);
return view('dashboard', compact('stats', 'activities'));
return view('posts.edit')
->with('post', $post)
->with('categories', $categories);
Echoing Data Safely
The double curly braces are the workhorse of Blade. Crucially, {{ }} runs its output through PHP's htmlspecialchars, so any HTML in the value is escaped โ your first and best defence against cross-site scripting (XSS).
{{-- Echo a variable โ automatically HTML-escaped (safe) --}}
{{ $name }}
{{-- Provide a fallback if the value is null --}}
{{ $title ?? 'Default Title' }}
{{-- Unescaped output โ renders raw HTML. Use ONLY for trusted content! --}}
{!! $trustedHtml !!}
{{-- A Blade comment: never appears in the sent HTML --}}
{{-- This note is compiled away --}}
โ ๏ธ The one rule to never break
Never pass user-supplied input through {!! !!}. Unescaped output of anything a user could control is a classic XSS hole. Reach for {!! !!} only for HTML you generated yourself and fully trust (for example, sanitised rich text from a Markdown pipeline).
Control Directives
Blade directives start with @ and mirror PHP's control structures with cleaner syntax. They are like a well-designed set of traffic signals, directing the flow of your template based on conditions.
{{-- Conditionals --}}
@if ($user->isAdmin())
<span class="badge">Admin</span>
@elseif ($user->isModerator())
<span class="badge">Moderator</span>
@else
<span class="badge">Member</span>
@endif
{{-- unless is the inverse of if --}}
@unless ($user->verified)
<div class="alert">Please verify your email.</div>
@endunless
{{-- Convenient authentication checks --}}
@auth
<p>Welcome back!</p>
@endauth
@guest
<a href="/login">Log in</a>
@endguest
{{-- Loops --}}
@foreach ($users as $user)
<p>{{ $user->name }}</p>
@endforeach
{{-- forelse handles the empty case gracefully --}}
@forelse ($posts as $post)
<article>{{ $post->title }}</article>
@empty
<p>No posts yet.</p>
@endforelse
Inside every @foreach, Blade exposes a $loop variable with handy metadata โ no more manual counters:
@foreach ($users as $user)
@if ($loop->first) <h3>Team</h3> @endif
<p class="{{ $loop->even ? 'row-even' : 'row-odd' }}">
{{ $loop->iteration }} of {{ $loop->count }} โ {{ $user->name }}
</p>
@if ($loop->last) <hr> @endif
@endforeach
๐ Handy $loop properties
$loop->index (0-based) ยท $loop->iteration (1-based) ยท $loop->first ยท $loop->last ยท $loop->even / $loop->odd ยท $loop->count ยท $loop->remaining ยท $loop->depth (for nested loops).
Template Inheritance
Most pages of a site share a header, footer, and shell. Blade's template inheritance lets you define that shell once as a layout and have each page fill in only its unique parts โ the essence of DRY (Don't Repeat Yourself).
{{-- resources/views/layouts/app.blade.php --}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>@yield('title', 'My Site')</title>
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
@stack('styles')
</head>
<body>
@include('partials.navigation')
<main class="container">
@yield('content')
</main>
@include('partials.footer')
@stack('scripts')
</body>
</html>
{{-- resources/views/pages/home.blade.php --}}
@extends('layouts.app')
@section('title', 'Home')
@section('content')
<h1>Welcome</h1>
<p>This page fills in only the content section.</p>
@endsection
| Directive | Role |
|---|---|
@yield('name', 'default') | In the layout: a placeholder a child fills, with an optional default |
@section โฆ @endsection | In the child: content that replaces a matching @yield |
@extends('layout') | Declares which layout a child page uses |
@include('partials.x') | Embeds another view (a reusable partial) |
๐ก Two styles, both valid
The @extends/@yield style shown here is the classic layout system. Modern Laravel also offers a component-based layout (<x-app-layout>). They achieve the same goal; new projects increasingly favour components, covered next.
Components & Slots
Components are reusable pieces of UI with a clear interface โ much like components in React or Vue. Define one, then drop it in wherever you need it. They are the prefabricated building sections of your views: made once, assembled anywhere.
Anonymous components
The simplest kind is just a Blade file in resources/views/components. Data arrives as attributes; the tag's inner content arrives as $slot.
{{-- resources/views/components/alert.blade.php --}}
<div class="alert alert-{{ $type ?? 'info' }}">
@isset($title)
<div class="alert-title">{{ $title }}</div>
@endisset
{{ $slot }}
</div>
{{-- Using it โ attributes map to variables, inner HTML fills $slot --}}
<x-alert type="danger" title="Error">
Something went wrong. Please try again.
</x-alert>
Named slots
A component can accept more than one block of content via named slots:
{{-- In the component --}}
<div class="card">
<div class="card-body">{{ $slot }}</div>
<div class="card-footer">{{ $footer }}</div>
</div>
{{-- Using it --}}
<x-card>
Main content here.
<x-slot:footer>
<a href="#">Read more</a>
</x-slot:footer>
</x-card>
Class-based components
When a component needs logic, back it with a class. The public properties and methods become available in its view.
<?php
// php artisan make:component Alert
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class Alert extends Component
{
public function __construct(
public string $type = 'info',
public ?string $title = null,
) {}
public function isImportant(): bool
{
return in_array($this->type, ['danger', 'warning']);
}
public function render(): View
{
return view('components.alert');
}
}
Pass data-bound attributes with a leading colon, exactly as in JS frameworks: <x-alert :type="$level" /> passes a PHP variable, while type="danger" passes a literal string.
Forms, CSRF & Errors
Blade makes secure forms almost automatic. Two directives handle the parts HTML forms cannot do on their own, and a third displays validation errors.
<form action="{{ route('posts.store') }}" method="POST">
@csrf {{-- injects a hidden CSRF token; required for all POST forms --}}
<div>
<label for="title">Title</label>
<input id="title" name="title"
value="{{ old('title') }}"
class="@error('title') is-invalid @enderror">
@error('title')
<p class="error">{{ $message }}</p>
@enderror
</div>
<button type="submit">Create Post</button>
</form>
HTML forms only support GET and POST. To send PUT, PATCH, or DELETE, spoof the method:
<form action="{{ route('posts.update', $post) }}" method="POST">
@csrf
@method('PUT') {{-- tells Laravel to treat this as a PUT request --}}
{{-- fields... --}}
<button type="submit">Update</button>
</form>
โ Three form helpers to memorise
@csrf protects against cross-site request forgery. old('field') refills inputs after a failed validation so users don't retype everything. @error('field') shows the message for that field. Together they give you safe, friendly forms with almost no boilerplate.
Stacks & Custom Directives
Stacks
Stacks let a child view push content โ usually page-specific scripts or styles โ into a named spot the layout defines. This keeps a page's JavaScript with the page that needs it, instead of loading everything everywhere.
{{-- In the layout: declare where pushed content lands --}}
<head>
@stack('styles')
</head>
<body>
@stack('scripts')
</body>
{{-- In a child view: push into those stacks --}}
@push('scripts')
<script src="{{ asset('js/chart.js') }}"></script>
@endpush
{{-- @once ensures a block runs a single time even across loops --}}
@once
@push('scripts')
<script src="{{ asset('js/chart-lib.js') }}"></script>
@endpush
@endonce
Custom directives
You can teach Blade new directives to wrap repetitive logic in clean syntax. Register them in a service provider's boot() method:
<?php
use Illuminate\Support\Facades\Blade;
public function boot(): void
{
Blade::directive('datetime', function (string $expression) {
return "<?php echo ({$expression})->format('M j, Y H:i'); ?>";
});
}
{{-- Then use it in any template --}}
Published: @datetime($post->created_at)
โ ๏ธ Playing nicely with JS frameworks
If a page also uses Vue or another {{ }}-based library, escape Blade with the @ prefix (@{{ jsVar }}) or wrap whole blocks in @verbatim โฆ @endverbatim so Blade leaves them for the browser to handle.
Hands-on Exercise
๐๏ธ A Layout, a Component, and a Page
Objective: Build a minimal but real Blade structure: a shared layout, a reusable alert component, and a page that extends the layout and uses the component.
Instructions:
- Create
resources/views/layouts/app.blade.phpwith a@yield('title'), a@yield('content'), and a@stack('scripts'). - Create an anonymous component
resources/views/components/alert.blade.phpthat accepts atypeattribute and renders its$slot. - Create
resources/views/pages/welcome.blade.phpthat extends the layout, sets a title, and shows a success alert plus a list looped over a$featuresarray. - Return the page from a controller:
return view('pages.welcome', ['features' => ['Fast', 'Secure', 'Elegant']]);
๐ก Hint
Remember: @extends goes at the very top of the child view, @section('title', 'Welcome') can be a one-liner, and the alert is used as <x-alert type="success">โฆ</x-alert>. Loop the features with @foreach.
โ Example solution
{{-- resources/views/layouts/app.blade.php --}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>@yield('title', 'My App')</title>
</head>
<body>
<main class="container">
@yield('content')
</main>
@stack('scripts')
</body>
</html>
{{-- resources/views/components/alert.blade.php --}}
<div class="alert alert-{{ $type ?? 'info' }}">
{{ $slot }}
</div>
{{-- resources/views/pages/welcome.blade.php --}}
@extends('layouts.app')
@section('title', 'Welcome')
@section('content')
<h1>Welcome to the app</h1>
<x-alert type="success">
Your account is ready to go!
</x-alert>
<ul>
@foreach ($features as $feature)
<li>{{ $feature }}</li>
@endforeach
</ul>
@endsection
๐ฏ Quick Quiz
Question 1: What is the key difference between {{ $value }} and {!! $value !!}?
Question 2: In template inheritance, which directive marks a placeholder in the layout that a child view fills in?
Question 3: Why must every POST form include the @csrf directive?
Summary & Quiz
๐ Key Takeaways
- Blade compiles to cached PHP โ concise syntax with near-zero overhead.
{{ }}escapes output (safe); reserve{!! !!}for trusted HTML only.- Directives like
@if,@forelse,@auth, and the$loopvariable replace clumsy PHP in views. - Template inheritance and components keep views DRY and reusable.
@csrf,@method,old(), and@errorgive you secure, user-friendly forms with little code.
๐ Further Reading
- Laravel Docs โ Blade Templates
- Laravel Docs โ Blade Components
- Laracasts โ Blade Component Examples
- Laravel Docs โ CSRF Protection
๐ What's Next?
You can route requests, run controllers, and render views. The missing piece is data. Next up: Eloquent ORM and database migrations โ defining your schema in code and working with the database through elegant model objects.
๐ Nicely done!
Your views are now DRY, reusable, and secure. Time to give them real data to display.