Laravel Blade and Middleware

Blade templating, layouts and components, and writing and registering custom middleware.

Blade basics

Blade is Laravel's templating engine — plain PHP files with a .blade.php extension and a concise syntax for output, control flow, and layouts. Blade templates compile down to plain PHP (and are cached), so there's no runtime performance cost compared to writing raw PHP by hand.

Blade
{{-- resources/views/posts/show.blade.php --}}
<h1>{{ $post->title }}</h1>
<p>{{ $post->body }}</p>

@if ($post->published)
    <span>Published</span>
@else
    <span>Draft</span>
@endif
PHP
<?php
// in a controller

return view('posts.show', ['post' => $post]);

{{ $post->title }} outputs the value HTML-escaped by default (via PHP's htmlspecialchars) — protecting against XSS the same way Jinja2's auto-escaping does in Flask/Django. Use {!! $raw !!} only for content you've deliberately sanitized or generated yourself, never for raw user input.

Control structures

Blade gives PHP's control structures a cleaner, template-friendly syntax:

Blade
@if ($posts->isEmpty())
    <p>No posts yet.</p>
@else
    <ul>
        @foreach ($posts as $post)
            <li>{{ $post->title }}</li>
        @endforeach
    </ul>
@endif

@foreach ($posts as $post)
    <p>{{ $loop->iteration }}: {{ $post->title }}</p>
@endforeach

Inside any @foreach, the special $loop variable exposes useful metadata — $loop->index (0-based), $loop->iteration (1-based), $loop->first, $loop->last, and more — without you having to track a counter manually.

Layouts with @extends and @section

Just like Jinja2 template inheritance in Flask/Django, Blade lets you define a shared page shell once and fill in only what changes per page:

Blade
{{-- resources/views/layouts/app.blade.php --}}
<!DOCTYPE html>
<html>
<head>
    <title>@yield('title', 'My App')</title>
</head>
<body>
    <nav>Home | Posts | Contact</nav>
    <main>
        @yield('content')
    </main>
</body>
</html>
Blade
{{-- resources/views/posts/index.blade.php --}}
@extends('layouts.app')

@section('title', 'All Posts')

@section('content')
    <h1>Posts</h1>
    <ul>
        @foreach ($posts as $post)
            <li>{{ $post->title }}</li>
        @endforeach
    </ul>
@endsection

@extends('layouts.app') must be the first line of the child template. @yield('content') in the layout marks a slot; @section('content') ... @endsection in the child fills it. @yield('title', 'My App') shows a default value used whenever a child template doesn't define that section.

Components

For a reusable piece of UI (a card, an alert box, a button), a Blade component is cleaner than repeating the same markup with @include everywhere:

Blade
{{-- resources/views/components/alert.blade.php --}}
<div class="alert alert-{{ $type }}">
    {{ $slot }}
</div>
Blade
<x-alert type="success">
    Your post was saved!
</x-alert>

type becomes a variable available inside the component, and {{ $slot }} renders whatever content was placed between the component's opening and closing tags — much like a reusable HTML element with its own attributes and children.

What middleware is

Middleware provides a way to filter and act on HTTP requests as they pass through the application — before they reach a route's controller, after the response is generated, or both. It's the standard place for cross-cutting concerns that apply across many routes at once: authentication checks, logging, rate limiting, enforcing HTTPS.

A real example: an admin-only check

Bash
php artisan make:middleware EnsureUserIsAdmin
PHP
<?php
// app/Http/Middleware/EnsureUserIsAdmin.php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureUserIsAdmin
{
    public function handle(Request $request, Closure $next): Response
    {
        if (! $request->user() || ! $request->user()->is_admin) {
            abort(403, 'Unauthorized.');
        }

        return $next($request);
    }
}

$next($request) passes control to the next layer of middleware (or, once the stack is exhausted, to the route's controller) — everything before that call runs before the route handles the request, and anything after it runs after, on the way back out. Calling abort(403, ...) instead of $next($request) stops the request from reaching the controller at all.

Registering middleware

In Laravel 11+, middleware is registered in bootstrap/app.php rather than a separate Kernel.php class:

PHP
<?php
// bootstrap/app.php

use App\Http\Middleware\EnsureUserIsAdmin;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->alias([
            'admin' => EnsureUserIsAdmin::class,
        ]);
    })
    ->create();

With an alias registered, apply the middleware to any route (or group of routes) by name:

PHP
<?php
// routes/web.php

Route::get('/admin/dashboard', [DashboardController::class, 'index'])
    ->middleware('admin');

Route::middleware(['auth', 'admin'])->group(function () {
    Route::get('/admin/users', [AdminUserController::class, 'index']);
    Route::get('/admin/settings', [AdminSettingController::class, 'index']);
});

Middleware can be stacked — ['auth', 'admin'] runs the built-in auth check first (confirming the user is logged in at all), then the custom admin check, before either route's controller runs.

Common mistakes

  • Forgetting to call $next($request) inside a middleware's handle() method — the request simply never reaches the route it was meant to protect (or pass through).
  • Doing heavy logic checks in a Blade template instead of the controller — Blade should render prepared data, not contain business logic like database queries.
  • Using {!! !!} to render user-supplied input directly — this disables Blade's automatic escaping and reopens the exact XSS risk it exists to prevent.

Interview questions

Q: What is middleware, in concrete terms, and give a real example of when you'd write custom middleware. Middleware is a layer that runs before a request reaches its route's controller, after the response leaves it, or both — it's how Laravel implements cross-cutting request filtering. A real example is an admin-only route guard: a middleware checks whether the authenticated user has admin privileges and calls abort(403) if not, or otherwise forwards the request onward via $next($request) — centralizing that check in one place instead of repeating it at the top of every admin controller method.

Q: What's the practical difference between Blade and writing plain PHP directly in a view file? Blade's @if, @foreach, @extends/@section and {{ }} (auto-escaping) syntax is more concise and readable than the equivalent raw <?php ... ?> blocks, while still compiling down to plain, cached PHP with no runtime performance penalty. Blade also adds specific conveniences plain PHP doesn't give you out of the box, like inheritance-based layouts and reusable components (<x-alert>), which would otherwise require hand-rolled include() calls and manual escaping discipline.