Laravel Blade Templating Reference

A reference for Laravel's Blade templating engine: directives, layouts, and components.

Layouts

A layout defines the shared page shell (nav, footer, scripts) once; individual views extend it and fill in named sections. This entire site's layouts.app works exactly this way.


{{-- resources/views/layouts/app.blade.php --}}
<body>
    @yield('content')
</body>

{{-- resources/views/posts/show.blade.php --}}
@extends('layouts.app')

@section('content')
    <h1>{{ $post->title }}</h1>
@endsection

Output and escaping


{{ $variable }}          {{-- HTML-escaped, safe for user input --}}
{!! $rawHtml !!}         {{-- NOT escaped, only for trusted HTML --}}
{{ $name ?? 'Guest' }}    {{-- with a default fallback --}}

Control structures


@if ($user->isAdmin())
    Admin
@elseif ($user->isEditor())
    Editor
@else
    Viewer
@endif

@foreach ($posts as $post)
    {{ $post->title }}
@endforeach

@forelse ($posts as $post)
    {{ $post->title }}
@empty
    No posts yet.
@endforelse

@auth
    Welcome back, {{ auth()->user()->name }}
@endauth

@guest
    <a href="/login">Log in</a>
@endguest

@switch($post->status)
    @case('draft')
        Draft
        @break
    @case('published')
        Published
        @break
    @default
        Unknown
@endswitch

@auth/@guest only check whether a user is logged in — for checking what a logged-in user is allowed to do, see the @can/@cannot directives in the policies and gates guide.

Inside any @foreach, Blade injects a $loop variable with useful state about the current iteration — no manual counter needed:


@foreach ($posts as $post)
    {{ $loop->index }}    {{-- 0-based position --}}
    {{ $loop->iteration }} {{-- 1-based position --}}
    @if ($loop->first) First! @endif
    @if ($loop->last) Last! @endif
    {{ $loop->count }}    {{-- total items --}}

    @foreach ($post->comments as $comment)
        {{ $loop->parent->index }}  {{-- reach the outer loop from a nested one --}}
    @endforeach
@endforeach

Including partials

An @include simply inlines another view's compiled output at that point in the template — it shares the including view's variable scope automatically, which is convenient but also means a typo'd variable name silently falls through to $undefinedVariable ?? null issues rather than an error. Repeating an include inside a loop compiles and evaluates it fresh every iteration, which is fine for small partials but worth remembering if the partial does anything expensive.


@include('partials.alert')

@include('partials.alert', ['type' => 'success'])

@includeWhen($errors->any(), 'partials.error-summary')

Use @once to guard a block so it only renders on the first pass through a given render cycle — handy when a component or partial that's included many times on one page needs to push a shared script or style tag exactly once:


@once
    @push('scripts')
        <script src="/js/tooltip.js"></script>
    @endpush
@endonce

Forms

The @error directive below reads from the same $errors bag Laravel flashes to the session after a failed validate() call — see the validation rules guide for how those rules are defined.


<form method="POST" action="/posts">
    @csrf
    <input type="text" name="title">
    @error('title')
        <span class="text-danger">{{ $message }}</span>
    @enderror
</form>

{{-- Spoof PUT/PATCH/DELETE, since HTML forms only support GET/POST --}}
<form method="POST" action="/posts/1">
    @csrf
    @method('PUT')
</form>

Components

Reusable pieces of markup with their own data and, optionally, a backing class.

php artisan make:component Alert

{{-- resources/views/components/alert.blade.php --}}
<div class="alert alert-{{ $type }}">
    {{ $slot }}
</div>

{{-- usage --}}
<x-alert type="danger">
    Something went wrong.
</x-alert>
class Alert extends Component
{
    public function __construct(
        public string $type = 'info',
    ) {}

    public function render(): View
    {
        return view('components.alert');
    }
}

Declare expected props explicitly with @props in an anonymous (class-less) component:


{{-- resources/views/components/alert.blade.php --}}
@props(['type' => 'info'])

<div {{ $attributes->merge(['class' => "alert alert-$type"]) }}>
    {{ $slot }}
</div>

Components differ from includes in a few important ways: props are declared explicitly rather than inherited implicitly from the caller's scope, they can carry a backing class with real logic and dependency injection, and Blade compiles each one as a distinct object rather than re-inlining a template string. Reach for a component when the piece of UI has its own data or behaviour worth naming; reach for an include when you're just splitting a large view into smaller files that still share the same variables. A nested component can read a prop from its parent without it being passed down explicitly by marking it @aware:


{{-- x-menu passes down a "color" prop; x-menu.item wants to reuse it --}}
@props(['color' => 'gray'])
@aware(['color' => 'gray'])

Named slots let a component accept more than one block of markup:


{{-- resources/views/components/card.blade.php --}}
<div class="card">
    <div class="card-header">{{ $title }}</div>
    <div class="card-body">{{ $slot }}</div>
</div>

{{-- usage --}}
<x-card>
    <x-slot:title>Account Settings</x-slot:title>
    Your default slot content goes here.
</x-card>

Conditional classes and attributes

The @class directive builds a class string from an array, applying each key only when its value is truthy — clearer than concatenating strings with ternaries, and it plays nicely with $attributes->merge() on components.


<div @@class([
    'alert',
    'alert-danger' => $errors->any(),
    'alert-success' => ! $errors->any(),
])>

A similar @checked, @selected, @disabled, and @readonly family of directives conditionally prints the matching HTML attribute, which is a common need on form re-renders after a failed validation.

Stacks

Let a child view push content (usually scripts or styles) up into a placeholder defined in the layout — used throughout this site's own tool pages for per-page <style>/<script> blocks.


{{-- layout --}}
@stack('scripts')

{{-- child view --}}
@push('scripts')
    <script>console.log('hi');</script>
@endpush

Custom directives

Register your own directive when a piece of formatting logic shows up in views often enough to deserve a name — usually in a service provider's boot() method:

use Illuminate\Support\Facades\Blade;

Blade::directive('money', function (string $expression) {
    return "<?php echo number_format({$expression} / 100, 2); ?>";
});

@money($post->price_in_pence)

A directive's callback receives the raw expression as a string and must return valid PHP — it's a thin wrapper around string generation, not a place to run application logic directly.

comments are stripped from the compiled output entirely, unlike an HTML <!-- comment --> which still reaches the browser (and the page's visitors) — use the Blade form for anything that shouldn't be visible in "view source".

Compilation and caching

Blade templates aren't interpreted directive-by-directive on every request. The first time a view is rendered, Laravel compiles it down to plain PHP and writes the result to storage/framework/views; every subsequent request just runs that cached PHP file directly, so the templating layer adds effectively no runtime overhead once warmed. Laravel checks the compiled file's timestamp against the source .blade.php file and recompiles automatically whenever the source changes, so this is invisible during normal development.

The one place it does bite is production deploys behind a cache that was primed against an old copy of the code — if the compiled cache directory isn't cleared as part of the deploy, stale views can serve briefly. php artisan view:clear deletes the compiled files, and php artisan view:cache pre-compiles every view up front so the very first request after a deploy isn't the one paying the compilation cost.

php artisan view:clear
php artisan view:cache