Reader Stacks

Creating a Custom Blade Directive in Laravel

A Blade directive is ultimately just a string-to-string PHP code transformation registered with the compiler — it has no runtime behavior of its own beyond whatever raw PHP it expands into.

Creating a Custom Blade Directive in Laravel

A custom Blade directive extends the template syntax itself — genuinely useful for a repeated pattern (formatting a price, checking a custom permission) that would otherwise need the same verbose PHP or conditional repeated across many views.

Registering a simple directive

// AppServiceProvider boot() method
use Illuminate\Support\Facades\Blade;

public function boot(): void
{
    Blade::directive('currency', function ($expression) {
        return "";
    });
}
@currency($product->price)

The directive's callback receives the expression inside the parentheses as a raw string and must return a string of valid PHP code — Blade inserts this returned PHP directly into the compiled template, which is why the callback effectively writes PHP code as text, rather than executing anything itself.

A directive with a conditional (if/endif pair)

Blade::if('role', function (string $role) {
    return auth()->check() && auth()->user()->hasRole($role);
});
@role('admin')
    Admin Panel
@endrole

Blade::if() is a higher-level helper specifically for creating conditional directives — it automatically generates the matching @role/@endrole (and @elserole) pair from a single boolean-returning callback, without needing to manually write the raw PHP if/endif code that Blade::directive() would require for the same result.

A directive that accepts multiple arguments

Blade::directive('datetime', function ($expression) {
    return "format('M d, Y \a\\t g:i A'); ?>";
});
@datetime($order->created_at)

A block-style directive, with a matching end tag

Blade::directive('cache', function ($expression) {
    return "";
});

Blade::directive('endcache', function ($expression) {
    return "";
});

A directive pair like this uses PHP's own output buffering (ob_start()/ob_get_clean()) to capture everything rendered between the two directives — genuinely more advanced than a single-expression directive, and worth reaching for only when the directive needs to wrap a block of template content, not just transform one value.

Where to register custom directives in a real project

Registering directives inside AppServiceProvider::boot() (as shown) works fine for a small number of them — for a project accumulating many custom directives, extracting them into a dedicated service provider (like a BladeServiceProvider) keeps AppServiceProvider from growing cluttered with unrelated registration logic.

Why a directive isn't always the right tool

A directive is genuinely worth creating for something used across many different views — for logic specific to just one or two templates, a plain @if with a model method or accessor (like $product->isOnSale()) is simpler and doesn't require registering anything globally just to be understood by another developer reading the template.