wemersonrv's avatar

Laravel Pint is not properly indenting custom Blade directives

My Laravel Pint setup is not indenting some custom Blade directives correctly.

I am using custom Blade directives that behave like structural blocks (similar to @if, @foreach, etc.). However, when running Pint, the indentation inside these directives is removed or flattened.

Here is a simplified example.

Original Blade code (properly indented)

<div class="mt-8 grid grid-cols-3 gap-6 relative">
    @island(name: 'metrics', defer: true, always: true)
        @placeholder
            <flux:skeleton class="h-30" animate="shimmer"/>
            <flux:skeleton class="h-30" animate="shimmer"/>
            <flux:skeleton class="h-30" animate="shimmer"/>
        @endplaceholder

        @island(always: true)
            <div wire:poll.5s>
                <livewire:pages::analytics.metric heading="Views" :number="$this->views" :change="12"/>
            </div>
        @endisland

        <livewire:pages::analytics.metric heading="Visitors" :number="$this->visitors" :change="8"/>
        <livewire:pages::analytics.metric heading="Avg time on post" :number="$this->avgTime" :change="-3"/>
    @endisland

    <div class="absolute top-0 bottom-0 left-[97.7%] pl-4 flex">
        <flux:button wire:click="$refresh" wire:island="metrics" icon="arrow-path" variant="subtle" size="sm" class="cursor-pointer"/>
    </div>
</div>

Result after running Pint

<div class="mt-8 grid grid-cols-3 gap-6 relative">
    @island(name: 'metrics', defer: true, always: true)
    @placeholder
    <flux:skeleton class="h-30" animate="shimmer"/>
    <flux:skeleton class="h-30" animate="shimmer"/>
    <flux:skeleton class="h-30" animate="shimmer"/>
    @endplaceholder

    @island(always: true)
    <div wire:poll.5s>
        <livewire:pages::analytics.metric heading="Views" :number="$this->views" :change="12"/>
    </div>
    @endisland

    <livewire:pages::analytics.metric heading="Visitors" :number="$this->visitors" :change="8"/>
    <livewire:pages::analytics.metric heading="Avg time on post" :number="$this->avgTime" :change="-3"/>
    @endisland

    <div class="absolute top-0 bottom-0 left-[97.7%] pl-4 flex">
        <flux:button wire:click="$refresh" wire:island="metrics" icon="arrow-path" variant="subtle" size="sm" class="cursor-pointer"/>
    </div>
</div>

As shown above, the indentation inside custom directives from Livewire, like @island and @placeholder is lost after formatting.

It appears that Pint does not recognize these custom directives as structural Blade blocks, so it does not treat them as indentation scopes.

This issue may affect any project that defines custom Blade directives intended to behave like block-level control structures.

Is there currently a way to register custom Blade directives as structural blocks so that Pint can correctly indent their contents?

0 likes
2 replies
LaryAI's avatar
Level 58

Solution:

This is currently a known limitation of Laravel Pint and its underlying Blade formatter (internally, Pint uses blade-formatter). Out of the box, neither Pint nor blade-formatter understands user-defined/custom block directives like @island and @placeholder as indentation scopes, so it "flattens" their contents.

Why This Happens

  • Pint/blade-formatter only recognizes built-in Blade structural directives: @if, @foreach, @section, etc.
  • Custom directives must be explicitly declared as block directives to the formatter for proper indentation.

Workaround / Solution

The good news: as of blade-formatter v2.5.0, custom block directive support exists via configuration!

Using Pint 1.5.x+ (and blade-formatter v2.5+ under the hood)

You can define a blade-formatter.json config file at your project root.
(If you're using Pint 1.5.x+, you can do this!)

Example: blade-formatter.json

{
  "indentSize": 4,
  "customBlockDirectives": [
    "island",
    "endisland",
    "placeholder",
    "endplaceholder"
  ]
}

However, you should only list opening block directives, not the end... close.
So usually:

{
  "customBlockDirectives": [
    "island",
    "placeholder"
  ]
}

How to set up with Laravel Pint

  1. Create blade-formatter.json in your project root:

    {
      "customBlockDirectives": [
        "island",
        "placeholder"
      ]
    }
    
  2. Update Pint (if necessary) and run Pint:

    composer update laravel/pint
    

    Now when you run Pint, it will pass the settings to the underlying blade-formatter.

If you use blade-formatter directly (CLI)

You can instead pass the directives as arguments:

blade-formatter --custom-block-directives "island,placeholder" resources/views/

Current Limitations

  • In Pint 1.0 - 1.4.x, you cannot pass custom block directives—the feature was added to blade-formatter later. Upgrade Pint if possible.
  • Only the opening directive name is needed; blade-formatter auto-matches @end<directive>.
  • Not all nested custom directives work perfectly, but basic block-level nesting is supported.

Summary

No, there is not a way to do this out of the box. But you CAN configure custom block directives by creating a blade-formatter.json in your project root and specifying them as customBlockDirectives. Pint 1.5+ passes this on to blade-formatter and your indentation will be fixed.


References:


Example directory structure:

project-root/
├── blade-formatter.json
├── resources/
│   └── views/
│       └── analytics.blade.php
├── ...

After configuring, run:

vendor/bin/pint

And you should get correct indentation for your custom directives! 🎉


Let me know if this doesn't solve your case or if you need Pint to support this directly in its own config.

Please or to participate in this conversation.