Updated June 2026. Tested on Laravel 13 and PHP 8.4.

When you find the same chunk of logic creeping into your Blade templates, an if block repeated across many views, it is a sign to wrap it in a custom directive. A directive hides that logic behind a clean @name, so your templates stay readable. Here is how to make your own.

Define a directive

Register directives in the boot method of a service provider, usually App\Providers\AppServiceProvider.

use Illuminate\Support\Facades\Blade;

public function boot(): void
{
    Blade::directive('hello', function (string $expression) {
        return "<?php echo 'Hello ' . {$expression}; ?>";
    });
}

Now you can use it in any template.

<p>@hello('World')</p>   {{-- renders: Hello World --}}

The closure receives whatever was inside the parentheses as a string, and returns a string of PHP code that Blade compiles into the view.

A real example: premium content

Say you run a subscription site and want to show premium content only to subscribers, and hide ads from them. Doing an if in every view gets old. Make a directive instead, so the template reads cleanly.

<div class="premium-video">
    @subscribed
        <video src="/premium.mp4"></video>
    @unsubscribed
        <p>You need a subscription to watch this.</p>
    @endsubscribed
</div>

The cleanest way to build this trio of if, else and end is Blade::if, which generates all three for you from a single condition.

use Illuminate\Support\Facades\Blade;

public function boot(): void
{
    Blade::if('subscribed', function () {
        return auth()->check() && auth()->user()->isSubscribed();
    });
}

That one call gives you @subscribed, @unsubscribed (the else branch) and @endsubscribed. Before Blade::if existed you had to register three separate directives by hand, so always reach for Blade::if when your directive is really a conditional.

Three things to watch

The expression is a string, not separate arguments. If you write @greet('Hi', 'Hammad'), the whole 'Hi', 'Hammad' arrives as one string. Split it yourself if you need the parts.

Blade::directive('greet', function (string $expression) {
    return "<?php [\$greeting, \$name] = [{$expression}]; echo \$greeting . ' ' . \$name; ?>";
});

Escape output. Inside {{ }} Blade escapes for you, but a custom directive does not. Wrap user data in e() to avoid injecting unescaped HTML.

Blade::directive('hello', function (string $expression) {
    return "<?php echo 'Hello ' . e({$expression}); ?>";
});

Clear the cache after changes. Blade compiles views and caches them. When you change a directive, the old compiled views stick around until you clear them.

php artisan view:clear

That is custom Blade directives: Blade::directive for general cases, Blade::if for conditionals, and a few gotchas around escaping and the view cache. Use them to keep logic out of your templates. Questions welcome in the comments.