Laravel Blade templates can look deceptively simple.
At first glance, a .blade.php file is mostly HTML. That can make formatting one seem like the same problem as formatting an ordinary HTML document.
It isn't.
A Blade template can combine HTML with conditional directives, loops, components, slots, PHP expressions and Laravel-specific syntax, sometimes within the same few lines.
Consider a deliberately badly formatted example:
<div class="user-card">
@if($user)
@if($user->active)
<h2>{{ $user->name }}</h2>
<p>{{ $user->email }}</p>
@if($user->isAdmin())
<span class="badge">Administrator</span>
@elseif($user->isEditor())
<span class="badge">Editor</span>
@else
<span class="badge">User</span>
@endif
@else
<p>This account is inactive.</p>
@endif
@else
<p>No user is available.</p>
@endif
</div>
The HTML itself is not particularly complicated. The difficulty comes from understanding the structure created by the Blade directives around it.
A formatter needs to recognise that @if, @elseif, @else and @endif are related control structures. It needs to understand how the HTML is nested inside those structures while also preserving Blade expressions such as {{ $user->name }}.
Simply treating the file as HTML is therefore not enough.
What a Blade-aware formatter needs to understand
Formatting HTML is largely concerned with the hierarchy of elements.
For example:
<div>
<section>
<p>Hello</p>
</section>
</div>
The opening and closing tags provide most of the information needed to understand the structure.
Blade introduces another structural layer.
@if ($user)
<div>
@foreach ($user->notifications as $notification)
<p>{{ $notification->message }}</p>
@endforeach
</div>
@endif
There are now at least three things a formatter needs to reason about:
- The HTML structure.
- The Blade directive structure.
- The expressions embedded within the template.
Real templates can become considerably more complicated when components, slots, conditional attributes, sections and other Blade features are introduced.
<x-layout>
<x-slot:title>
{{ $pageTitle }}
</x-slot:title>
@if ($user->isAdmin())
<x-admin.dashboard :user="$user" />
@else
<x-user.dashboard :user="$user" />
@endif
</x-layout>
A formatter that only understands HTML does not have a complete picture of that document.
What should our badly formatted example become?
Running the original example through a Blade-aware formatter produces a much clearer structure:
<div class="user-card">
@if ($user)
@if ($user->active)
<h2>{{ $user->name }}</h2>
<p>{{ $user->email }}</p>
@if ($user->isAdmin())
<span class="badge">Administrator</span>
@elseif ($user->isEditor())
<span class="badge">Editor</span>
@else
<span class="badge">User</span>
@endif
@else
<p>This account is inactive.</p>
@endif
@else
<p>No user is available.</p>
@endif
</div>
Nothing particularly exciting happened to the application logic.
That's the point.
Formatting isn't supposed to redesign the template. It makes its existing structure easier for a human to see.
The relationship between the outer user check, the account status check and the role check is now immediately apparent.
There is also a smaller change that is easy to overlook:
@if($user)
became:
@if ($user)
Consistent spacing and indentation might seem cosmetic, but across a large collection of views they reduce unnecessary variation and make templates easier to scan.
Formatting is not linting
This distinction is important.
A formatter can make code consistent without determining whether that code represents a good implementation.
For example, this can be beautifully formatted:
@if ($conditionOne)
@if ($conditionTwo)
@if ($conditionThree)
@if ($conditionFour)
...
@endif
@endif
@endif
@endif
But formatting does not answer whether four levels of nested conditions belong in the template.
Perhaps some of that logic should move elsewhere. Perhaps a Blade component would make the view clearer. Perhaps the underlying design needs reconsideration.
Similarly, a formatter cannot necessarily tell you that:
- a route name does not exist;
- a variable was never passed to the view;
- a component is missing;
- an included view does not exist;
- the business logic is incorrect.
Formatting improves presentation. It should not be confused with validation, static analysis, testing or application design.
Blade formatting options
There are several ways to approach Blade formatting.
Editor extensions can format templates as part of the development workflow. Blade-aware Prettier tooling can provide automated formatting, and dedicated Blade formatter projects are available for developers who want formatting integrated into their local environment.
Laravel Pint also supports Blade formatting.
Blade formatting is not enabled by default in Pint, but it can be invoked with the --blade option or configured using the Pint/laravel_blade rule.
For a Laravel project where Pint is already part of the development workflow, that can be a natural place to enforce formatting consistently.
There are also situations where you may not want to configure anything locally.
Perhaps you have been sent a Blade fragment.
Perhaps you are reviewing an example outside the original project.
Perhaps you are working on a machine without the project's development environment.
Or perhaps you simply want to paste a template somewhere, format it and copy the result.
That is where a browser-based formatter can be useful.
Building a browser-based Blade formatter
While developing SwiftVecto, I wanted the Laravel Blade Formatter to work without requiring the user to upload a Blade file or configure a Laravel project.
The formatter therefore operates directly in the browser using Blade-aware formatting tooling.
The basic workflow is intentionally simple:
Blade source
↓
Blade-aware formatter
↓
Formatted template
↓
Copy result
The tool also analyses the source separately to provide information such as directive counts, echo expressions, components, slots, loops, conditionals, sections and approximate directive nesting depth.
Those statistics aren't part of formatting itself. They are simply another way of getting a quick picture of the structure of a template.
The important architectural distinction is that normal interactive formatting is performed in the browser rather than sending the Blade source to the server for processing.
Creating reusable formatting fixtures
While testing formatting behaviour, another useful idea emerged.
Instead of keeping test examples buried inside development notes, why not make them reusable?
I created a public SwiftVecto repository for Blade formatting examples and fixtures.
A simple fixture structure can contain:
fixtures/
├── unformatted/
│ └── conditionals.blade.php
└── expected/
└── conditionals.blade.php
The unformatted version provides a reproducible input.
The expected version provides a reference result.
That makes it easier to experiment with different formatting workflows and, as the fixture collection grows, test more interesting Blade structures.
The expected output should not necessarily be treated as the one canonical way every Blade template must look. Formatter versions, configuration and formatting rules can legitimately produce different results.
The important part is having reproducible examples.
Try the example yourself
The example used in this article is available as part of the public Blade formatting fixtures:
View the Blade formatting examples on GitHub
You can also paste the unformatted example directly into the browser-based formatter:
Try the SwiftVecto Laravel Blade Formatter
No Laravel project is required to experiment with it.
Final thoughts
Formatting will not make a Blade template well designed.
It will not fix application logic, replace testing or tell you whether a view has become too complicated.
What it can do is remove unnecessary visual inconsistency and make the structure that already exists considerably easier to reason about.
And with Blade, that structure involves more than HTML.
As the public fixture collection grows, I plan to add examples covering components, slots, loops, layouts, forms, convenience directives and some of the more awkward structures that can appear in real Blade templates.
If you have an interesting Blade formatting edge case, I'd be interested to see it.
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.