Blade Template Engine: Eliminating Repetitive HTML
The practical payoff of separating the view layer from logic: using Blade layouts, sections, and partials.
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 automatically escapes 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() automatically, which makes security the default rather than an afterthought.
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 block with @php:
@php
$total = $price * $quantity;
@endphp
<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 always 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 XSS protection on by default means security is no longer something 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.