Blade Template Engine: Eliminating Repetitive HTML
The practical payoff of separating the view layer from logic: using Blade layouts, sections, and partials.
Written 12 years ago — it reflects the conditions of that time.
I’m still deep in Laravel. This month I’ve been focused on the view layer. My old approach with PHP templates was straightforward: include header.php and footer.php on every page. That works fine on small projects, but as things grow, tracking which page includes what becomes a real chore.
Blade is Laravel’s templating engine. Unlike raw PHP templates, it provides clean syntax for template inheritance and partials.
Why a template engine?
In raw PHP templates, HTML and PHP code get tangled together. In a long page it’s easy to get lost between <?php foreach(...): ?> and <?php endforeach; ?>. Blade makes this more readable with directives like @foreach and @endforeach. It also gives you a short syntax, {{{ }}}, for escaping output against XSS (Cross-Site Scripting).
With the old approach, writing <?= $user->name ?> gave me zero XSS protection — I was printing user-supplied data straight to the page. Blade’s {{{ $user->name }}} syntax calls htmlspecialchars() for you, which boils escaping down to a single piece of syntax.
Creating a master layout
First I create the base skeleton — the layout file: app/views/layouts/main.blade.php
<!DOCTYPE html>
<html>
<head>
<title>@yield('title') | My Site</title>
<link rel="stylesheet" href="/css/app.css">
</head>
<body>
@include('partials.menu')
<div class="content">
@yield('content')
</div>
@include('partials.footer')
</body>
</html>
@yield('content') defines a placeholder where child pages can inject their own content. I use @include to move repeated sections into separate files.
Child views
app/views/home.blade.php:
@extends('layouts.main')
@section('title', 'Home')
@section('content')
<h1>Welcome</h1>
<p>This is the home page.</p>
@foreach($news as $item)
<div class="news-item">
<h2>{{{ $item->title }}}</h2>
<p>{{{ $item->summary }}}</p>
</div>
@endforeach
@endsection
@extends('layouts.main') declares which layout this view uses. @section fills the @yield slots defined in that layout. The {{{ $variable }}} syntax escapes HTML special characters and produces safe output.
Conditionals and loops
Blade directives map one-to-one with their raw PHP counterparts:
@if($user->isLoggedIn())
<a href="/logout">Logout</a>
@else
<a href="/login">Login</a>
@endif
@forelse($comments as $comment)
<p>{{{ $comment->body }}}</p>
@empty
<p>No comments yet.</p>
@endforelse
@forelse renders the @empty block when the list is empty. I reach for this combination constantly; previously I’d write a separate count($array) > 0 check.
Partials
Moving repeated pieces into separate files keeps things tidy. I put the navigation into app/views/partials/menu.blade.php:
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
@if(Auth::check())
<a href="/dashboard">Dashboard</a>
@endif
</nav>
Every page just writes @include('partials.menu') and Blade handles the rest.
Passing data from the controller to the view
There are a few ways to pass variables from a controller to a view:
// Using compact()
$news = News::all();
return View::make('home', compact('news'));
// Method chaining with with()
return View::make('home')->with('news', News::all());
Both produce the same result; compact() is more convenient when you’re passing multiple variables at once.
Running raw PHP expressions
Occasionally you need PHP that doesn’t have a Blade directive. You can open a plain PHP tag inside the template:
<?php $total = $price * $quantity; ?>
<p>Total: {{ $total }}</p>
I use this sparingly — business logic should not creep into the view layer. But for simple calculations it gets the job done.
The escape hatch: rendering raw HTML
Blade’s {{{ }}} syntax escapes HTML — and that’s a good thing. But if you’re storing HTML content in the database and need to render it as-is, you use the {{ }} syntax:
{{ $article->content }}
This should only be used for content you fully trust. Rendering user-submitted input with {{ }} completely disables XSS protection. I learned this distinction on a CMS project: {{ }} was the right call for HTML articles coming from an editor, but using the same syntax for comment fields would have opened a security hole.
Compared to the old approach
With the old header.php / footer.php pattern, every page needed two includes and I had to keep their interdependencies in my head. Blade’s inheritance model inverts this: there’s a central layout file, and child pages attach to it. Adding a new page is far cleaner.
After a few weeks of use, I appreciate the structure a template engine brings. The @forelse and @include combination alone has noticeably reduced repetitive code. And having escaping boiled down to a short {{{ }}} syntax means security is no longer a htmlspecialchars() call you have to “remember” each time.
Blade has one more feature worth mentioning: the @stack and @push directives let you inject page-specific CSS or JavaScript blocks into a designated slot in the layout. For example, if a page requires a specific JavaScript library, you can push it from that page’s view into a @stack('scripts') slot defined just before the layout’s </body> tag. Instead of bundling all JS into one file, you include page-specific scripts only when that page loads.
Comments
Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.