radic/blade-extensions
radic/blade-extensions is a collection of practical custom directives that extend Laravel's Blade templating engine. It adds convenient helpers for variable assignment, loop control, and common view tasks, letting you keep templates cleaner and more expressive without writing extra PHP.
Install
composer require radic/blade-extensions
On Laravel 5.5+ the service provider (Radic\BladeExtensions\BladeExtensionsServiceProvider) is auto-discovered. On older apps, register it manually in config/app.php under providers.
Publish the config (you'll want this — many directives are toggleable and extensible):
php artisan vendor:publish --provider="Radic\BladeExtensions\BladeExtensionsServiceProvider"
Where to look first:
config/blade-extensions.php — this is the source of truth. It lists every directive, whether it's enabled, and where you can register your own.vendor/radic/blade-extensions/src/Directives/ — each directive is a small class; skim ForeachDirective, SetDirective, ArrayDirective first.First use case — the package's original selling point was $loop metadata inside @foreach, back before Laravel 5.3 shipped it:
@foreach($users as $user)
<li class="{{ $loop->first ? 'first' : '' }} {{ $loop->last ? 'last' : '' }}">
{{ $loop->iteration }}/{{ $loop->count }} — {{ $user->name }}
</li>
@endforeach
If you're on a modern Laravel, start instead with the two directives that still have no native equivalent: @set and @array.
@set($fullName, $first . ' ' . $last)
<h1>Hello, {{ $fullName }}</h1>
@set@set lets you name intermediate values inline in a view without dropping into <?php ?>:
@set($initials, strtoupper(substr($first, 0, 1) . substr($last, 0, 1)))
<div class="avatar" data-initials="{{ $initials }}">{{ $initials }}</div>
Both @set($var, $value) and @set($var = $value) work. Prefer the comma form — it avoids compile edge cases when the value contains a = or a comparison operator.
Pair with @unset($var) to keep the shared scope clean when the value is expensive and only one partial needs it.
@arrayUseful when you're passing a literal config object into an include or component and don't want to chain long array syntax into a one-liner:
@array($opts, [
'title' => 'Billing',
'route' => route('billing.index'),
'icon' => 'credit-card',
'active' => request()->routeIs('billing.*'),
])
@include('partials.card', $opts)
This is much clearer than @include('partials.card', ['title' => ..., 'route' => ..., ...]) for 4+ keys, and reads like a Twig set block.
@break / @continue)Handy for "first N matching items" or skipping empty entries — common when rendering menus or filtered lists where you only want three hits:
@foreach($posts as $post)
@continue(empty($post->excerpt))
<article>{{ $post->excerpt }}</article>
@break($loop->iteration >= 3)
@endforeach
@break and @continue accept a condition argument, so you can keep them one-line. Note these compile to break/continue inside the generated foreach, so they only work within a @foreach/@forelse block. Break out of both loops with @break(2) (n levels).
@include / @each / components@set and @array compose well with @include:
@foreach($teams as $team)
@set($memberCount, $team->members->count())
@array($badge, ['text' => $memberCount . ' members', 'tone' => $memberCount > 5 ? 'ok' : 'muted'])
@include('partials.badge', $badge)
@endforeach
You can also drop these into a <x-…> component's @props defaults, since the directives run at the Blade compile stage before the component is instantiated.
The real long-term value is the config-driven extension point. In config/blade-extensions.php you can register a class implementing the package's DirectiveContract:
'directives' => [
'money' => App\Blade\MoneyDirective::class,
],
Then @money($cents) compiles however you like — a clean way to add a project-specific @money/@date while still letting the package manage registration, caching, and the @end… pairing if you need it.
Laravel 5.3+ ships $loop (index, iteration, first, last, depth, parent, remaining, count) plus native @break, @continue. If you're on Laravel 8/9/10/11, only @set, @unset, and @array are still pulling their weight. Blindly installing this on a fresh app adds a service provider, a config file, and a directive compiler for features you already have. Audit before adopt.
If your app or another package already defines @set or @foreach (e.g. a theming package, or Livewire/Tall-stack helpers), the last registered directive wins — usually silently. Symptom: a directive you know is defined throws "unexpected end of file" or produces wrong output. Debug by dumping the compiled view (see below) and checking which path the @set compiled to. Disable the package's version in config/blade-extensions.php if you need to defer to someone else.
@set is compile-time, not runtime@set emits a plain PHP assignment in the compiled template — it does not create a scoped variable or a view-local. Consequences:
@foreach/@if blocks (PHP has no block scope).@set($labelText, …), not @set($title, …)).@include unless you pass it — includes have their own scope. Use @include('partial', ['x' => $x]) or @include('partial', get_defined_vars()).After editing config/blade-extensions.php you must run php artisan config:clear (or config:cache) — otherwise the old directive set is active in production and CI. If a directive "isn't defined" only in production, this is almost always why.
Blade compiles to storage/framework/views/. When a directive misbehaves:
php artisan view:clear
# then hit the page, and:
grep -n "some_var" storage/framework/views/*.php | tail
The compiled file shows exactly what @array/@set/@foreach emitted — the fastest way to spot a broken argument or an unexpected directive collision.
@array syntax sensitivityThe directive parses its second argument as a PHP array literal at compile time. Trailing commas, unescaped ] inside strings, or using @array($x = [...]) instead of @array($x, [...]) will produce broken PHP that only fails when the compiled view is included (a generic "syntax error, unexpected" with no line pointing at the Blade file). Keep arrays simple — no closures, no fn(), no new inside @array. For anything dynamic, compute it with @set first or use a view composer.
$loop shadowingThis package's @foreach defines its own $loop object. On Laravel 5.3+ that collides with the framework's $loop. If both are active you may see the wrong $loop->parent chain, or $loop being null inside a manually included partial that expects the native structure. Simplest fix on modern Laravel: disable the package's foreach directive entirely and keep only set/array.
The package receives infrequent updates. Pin it ("radic/blade-extensions": "^2.x"), and add a test that renders each view using the directives — a snapshot/assertSee test will catch a regression the moment you bump Laravel past a Blade compiler change.
If your only needs are loop metadata, @break, and @continue, uninstall it and use @php/@isset/native $loop. Reserve the package for teams that lean heavily on @set/@array as a Twig-like template style, or that want the config-based custom-directive registry.
How can I help you explore Laravel packages today?