Introduction
Blade directives are compact shortcuts for PHP control structures — conditionals, loops, switches, and attribute helpers. They make your templates readable without forcing you to wrap logic in <?php ?> tags.
Key Concepts
- Directive: A Blade keyword prefixed with
@(like@if,@foreach) that compiles to PHP. - Loop variable (
$loop): An object automatically exposed inside@foreach/@forelseblocks with properties likefirst,last,index,iteration, andparent. - Conditional class directive (
@class): A helper that conditionally concatenates CSS class names into the renderedclass=""attribute. - Form attribute directives (
@checked,@selected,@disabled,@readonly,@required): Helpers that render the matching HTML attribute only when a condition is true.
Real World Context
Every non-trivial view contains conditionals, loops, and form field bindings. Directives replace dozens of short PHP blocks per file, keeping templates focused on layout instead of logic. They're the reason Blade feels like a template language rather than PHP embedded in HTML.
Deep Dive
Blade provides many directives—shortcuts for common PHP control structures that make your templates more readable.
Conditional Directives
@if, @elseif, @else, @endif
blade@if (count($posts) === 0) <p>No posts available.</p> @elseif (count($posts) === 1) <p>One post found!</p> @else <p>{{ count($posts) }} posts found.</p> @endif
@unless (Inverse of @if)
blade@unless (auth()->check()) <a href="/login">Please log in</a> @endunless {{-- Same as: @if (!auth()->check()) --}}
@isset and @empty
blade@isset($user) <p>Welcome, {{ $user->name }}!</p> @endisset @empty($posts) <p>No posts to display.</p> @endempty
@auth and @guest
blade@auth <p>Welcome back, {{ auth()->user()->name }}!</p> <a href="/logout">Logout</a> @endauth @guest <a href="/login">Login</a> <a href="/register">Register</a> @endguest {{-- With specific guard --}} @auth('admin') <a href="/admin">Admin Panel</a> @endauth
@env
blade@env('local') <div class="debug-bar">Debug Mode</div> @endenv @env(['local', 'staging']) <div class="warning">Non-production environment</div> @endenv
Loops
@foreach
blade@foreach ($posts as $post) <article> <h2>{{ $post->title }}</h2> <p>{{ $post->excerpt }}</p> </article> @endforeach
@forelse (Handles Empty Collections)
blade@forelse ($posts as $post) <article> <h2>{{ $post->title }}</h2> </article> @empty <p>No posts available.</p> @endforelse
@for
blade@for ($i = 0; $i < 10; $i++) <p>Iteration {{ $i }}</p> @endfor
@while
blade@php $remaining = 3; @endphp @while ($remaining-- > 0) <p>{{ $remaining + 1 }} notification(s) remaining</p> @endwhile
@while is rare in templates — prefer @foreach over a counter. It's worth recognizing when you see it.
@continue and @break
blade@foreach ($posts as $post) @if ($post->hidden) @continue @endif <h2>{{ $post->title }}</h2> @if ($loop->iteration > 10) @break @endif @endforeach {{-- Shorthand with condition --}} @foreach ($posts as $post) @continue($post->hidden) <h2>{{ $post->title }}</h2> @break($loop->iteration > 10) @endforeach
The $loop Variable
Inside @foreach and @forelse, you have access to a special $loop variable:
blade@foreach ($posts as $post) @if ($loop->first) <p>First post!</p> @endif <article class="@if($loop->even) even @endif"> <span>{{ $loop->iteration }} of {{ $loop->count }}</span> <h2>{{ $post->title }}</h2> </article> @if ($loop->last) <p>Last post!</p> @endif @endforeach
Available $loop Properties
| Property | Description |
|---|---|
$loop->index | Current index (0-based) |
$loop->iteration | Current iteration (1-based) |
$loop->remaining | Iterations remaining |
$loop->count | Total items |
$loop->first | Is first iteration? |
$loop->last | Is last iteration? |
$loop->even | Is even iteration? |
$loop->odd | Is odd iteration? |
$loop->depth | Nesting level |
$loop->parent | Parent $loop in nested loops |
Nested Loops
blade@foreach ($categories as $category) <h2>{{ $category->name }}</h2> @foreach ($category->posts as $post) <p> Category {{ $loop->parent->iteration }}, Post {{ $loop->iteration }} </p> @endforeach @endforeach
Switch Statements
blade@switch($user->role) @case('admin') <span class="badge badge-red">Admin</span> @break @case('editor') <span class="badge badge-blue">Editor</span> @break @default <span class="badge">User</span> @endswitch
Class and Style Directives
blade{{-- Conditional classes --}} <div @class([ 'p-4', 'bg-red-500' => $isError, 'bg-green-500' => $isSuccess, 'font-bold' => $isImportant, ])> Message </div> {{-- Conditional styles --}} <div @style([ 'background-color: red' => $isError, 'font-weight: bold' => $isImportant, ])> Styled content </div>
Checked, Selected, Disabled
blade<input type="checkbox" name="active" @checked($user->active) /> <select name="status"> @foreach ($statuses as $status) <option value="{{ $status }}" @selected($status === $user->status)> {{ $status }} </option> @endforeach </select> <button @disabled($form->isProcessing)>Submit</button> <input type="text" @readonly($user->isAdmin) /> <input type="text" @required($isRequired) />
Common Pitfalls
- Reaching for
@if+@foreachwhen@forelsefits —@forelsehandles the empty case in one directive. Using@if (count($x)) @foreach ... @endforeach @endifis strictly worse. - Assuming
$loopexists outside loops — Accessing$loop->firstoutside a@foreach/@forelseis an undefined variable error. - Concatenating conditional classes manually — Writing
class="{{ $isActive ? 'active' : '' }}"is error-prone;@classis the idiomatic replacement.
Best Practices
- Use
@forelsefor iterations with an empty state — One directive, one branch, one intent. - Use
@classand@stylefor conditional attributes — They handle whitespace and empty-string cases cleanly. - Use
@auth/@guest/@envover manualauth()->check()comparisons — Shorter, more readable, and intention-revealing.
Summary
- Conditional directives (
@if,@unless,@isset,@auth,@env) replace PHP conditionals. - Loop directives (
@foreach,@forelse,@for,@while) expose the$loophelper for nested iteration context. @switchis a cleaner alternative to chained@if/@elseif.@class,@style,@checked,@selected,@disabled,@readonly,@requiredare form-friendly attribute helpers.