Laravel Framework

Blade Templating Engine

20 min Lesson 5 of 45

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:

  1. Create resources/views/layouts/app.blade.php with header, nav, main content area, sidebar, and footer
  2. Create partial views for navigation, sidebar, and footer in resources/views/partials/
  3. Use @yield for title and content sections
  4. Add @stack for styles and scripts
  5. Create a child view that extends this layout

Practice Exercise 2: Blog Post List

Create a blog posts listing page:

  1. Pass an array of posts from controller to view
  2. Use @forelse to loop through posts
  3. Display "No posts found" message when empty
  4. Add @if statements to show "New!" badge for posts created in last 7 days
  5. Use $loop variable to add "featured" class to first post
  6. Display alternating background colors using $loop->even

Practice Exercise 3: Alert Component

Create a reusable alert component:

  1. Create resources/views/components/alert.blade.php
  2. Accept props: type (success/error/warning/info), dismissible (boolean), title (optional)
  3. Use @props and $attributes->merge() for dynamic classes
  4. Add named slots for title and actions (buttons)
  5. 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.