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/@forelse blocks with properties like first, last, index, iteration, and parent.
  • Conditional class directive (@class): A helper that conditionally concatenates CSS class names into the rendered class="" 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

PropertyDescription
$loop->indexCurrent index (0-based)
$loop->iterationCurrent iteration (1-based)
$loop->remainingIterations remaining
$loop->countTotal items
$loop->firstIs first iteration?
$loop->lastIs last iteration?
$loop->evenIs even iteration?
$loop->oddIs odd iteration?
$loop->depthNesting level
$loop->parentParent $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

  1. Reaching for @if + @foreach when @forelse fits — @forelse handles the empty case in one directive. Using @if (count($x)) @foreach ... @endforeach @endif is strictly worse.
  2. Assuming $loop exists outside loops — Accessing $loop->first outside a @foreach/@forelse is an undefined variable error.
  3. Concatenating conditional classes manually — Writing class="{{ $isActive ? 'active' : '' }}" is error-prone; @class is the idiomatic replacement.

Best Practices

  1. Use @forelse for iterations with an empty state — One directive, one branch, one intent.
  2. Use @class and @style for conditional attributes — They handle whitespace and empty-string cases cleanly.
  3. Use @auth/@guest/@env over manual auth()->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 $loop helper for nested iteration context.
  • @switch is a cleaner alternative to chained @if/@elseif.
  • @class, @style, @checked, @selected, @disabled, @readonly, @required are form-friendly attribute helpers.
✓ Completed