VS Vikesh Singh

Courses PHP & Laravel Lesson 8

PHP & LARAVEL · LESSON 8 OF 18

Blade Templates: Building Views Without the Mess

· 4 min read

Lesson 7 covered routes and controllers, ending with controllers returning a `view()`.

Lesson 7 covered routes and controllers, ending with controllers returning a view(). This lesson is about what that view actually is: Blade, Laravel's templating engine, and how it keeps your HTML readable instead of turning into the tangled PHP-and-markup mess we warned about back in lesson 1.

Blade Basics And Escaping

A Blade file lives under resources/views/ and ends in .blade.php. It looks like ordinary HTML, with a small set of special syntax for inserting dynamic data and control flow:

<h1>Welcome, {{ $name }}</h1>

@if ($isAdmin)
    <p>You have admin access.</p>
@else
    <p>Standard account.</p>
@endif

The double curly braces {{ }} output a value, and critically, Blade escapes it automatically using htmlspecialchars under the hood. This single detail matters enormously for security. If $name contained something like <script>alert('hacked')</script>, submitted by a malicious user through a form, {{ $name }} would render it as harmless visible text, not as an executable script. This protects against a class of attack called cross-site scripting (XSS), one of the most common real-world web vulnerabilities. In plain PHP, you have to remember htmlspecialchars() yourself, every single time, for every single variable you output; forgetting even once creates a security hole. Blade removes that burden by escaping by default.

If you genuinely need to output raw, unescaped HTML (say, content from a trusted admin-authored field that already contains intentional HTML markup), Blade provides {!! !!}, but treat it as a loaded gun: only ever use it on content you fully trust, never on raw user input.

{!! $trustedHtmlContent !!}

Control Structures In Blade

Blade's control structures mirror plain PHP's if, foreach, and for, but with cleaner syntax:

@foreach ($posts as $post)
    <article>
        <h2>{{ $post->title }}</h2>
        <p>{{ $post->excerpt }}</p>
    </article>
@endforeach

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

@forelse deserves special mention because beginners often don't know it exists and write a separate if (count($posts) === 0) check above a regular @foreach instead. @forelse does both in one clean block: loop over the items, and automatically show the @empty section if the collection is empty, with no separate count check needed.

Layouts With Extends And Section

The single most valuable Blade feature, the one that actually solves the "tangled mess" problem from lesson 1, is template inheritance through layouts. Instead of repeating your <html>, <head>, navigation bar, and footer in every single view, you define them once in a layout file, and each page fills in just the parts that differ:

<!-- resources/views/layouts/app.blade.php -->
<!DOCTYPE html>
<html>
<head>
    <title>@yield('title', 'My Course Site')</title>
</head>
<body>
    <nav>Course by Vikesh Singh</nav>

    <main>
        @yield('content')
    </main>
</body>
</html>
<!-- resources/views/posts/show.blade.php -->
@extends('layouts.app')

@section('title', $post->title)

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

@extends says "build this page on top of that layout." @section fills in a named slot that the layout declared with @yield. The practical effect: change the navigation bar once, in the layout file, and every page in your entire application updates. This is the structural fix for the "HTML and logic tangled everywhere" problem that plain PHP makes so easy to fall into.

Components And Slots

Blade also supports reusable, self-contained pieces called components, useful for things like a styled button or a card that appears on many pages with slightly different content:

<!-- resources/views/components/alert.blade.php -->
<div class="alert alert-{{ $type }}">
    {{ $slot }}
</div>
<x-alert type="success">
    Welcome back, {{ $user->name }}!
</x-alert>

$slot automatically receives whatever content was placed between the opening and closing component tags. Components are the Blade-native alternative to copy-pasting the same chunk of markup across multiple views, and they keep styling and markup decisions in one place.

A common beginner mistake worth flagging directly: putting real business logic inside Blade files, database queries, complex calculations, or multi-step conditionals that belong in the controller. Blade can technically run almost any PHP you throw at it with @php ... @endphp, but just because it can doesn't mean it should. A view's job is to display data it was handed, not to go fetch or transform that data itself. If you find yourself writing a loop inside a loop with three nested @if statements inside a Blade file, that is a strong signal the logic belongs in the controller or in a dedicated class instead, with the view receiving an already-prepared, simple value.

A practical tip: Blade templates are compiled into plain PHP and cached automatically, so performance is not a real concern, but during local development, if you edit a Blade file and don't see the change reflected, check that you have not left stray cached views around with php artisan view:clear.

Includes Versus Components

Blade also provides @include for pulling in a smaller, reusable snippet of markup that does not need its own layout or named sections, useful for something like a repeated "post card" shown on both a homepage and a search results page:

@foreach ($posts as $post)
    @include('partials.post-card', ['post' => $post])
@endforeach

The difference between @include and a full component (shown earlier with <x-alert>) is mostly about how much structure you need: reach for @include for a simple snippet of markup, and reach for a component once you need named slots, attributes, or any real logic attached to the piece being reused.

Hands-on check: build a layouts.app file with a @yield('content') section, then build two separate pages that both @extend it with different content, and confirm in the browser that editing the shared navigation bar in the layout changes both pages at once.