All rules

Sheath rule

blade-no-directive-attribute-collision

Event-listener attributes whose name collides with a Blade directive (@error="...", @empty="...") compile as the directive, not the listener.
Package
Core
Category
Blade
Default severity
error by default
Auto-fix
Manual fix

#Why

Vue and Alpine spell event listeners @event="handler". Blade compiles statements before the browser or either framework sees the template, and it does not care that the @ sits inside a tag. When the event name is also a Blade directive, Blade wins:

<img :src="src" @error="handleError">
compiles to
<img :src="src" <?php if($errors->has(...)): ?>="handleError">

The listener never binds, and the compiled tag is corrupted PHP-in-markup. Any known directive name can collide this way: @error, @empty, @selected, @checked, @disabled, @section, and so on. Blade also consumes a known directive prefix before a kebab or colon suffix, so @csrf-token="..." and @csrf:token="..." corrupt the compiled tag too.

Names Blade does not know (@click, @submit.prevent, @keydown.escape) pass through untouched and are never reported. Blade directives used on purpose in attribute position (@checked($value), @class([...])) take arguments in parentheses, not ="...", and are never reported either.

#Examples

#Bad

<img :src="src" @error="handleError">
<div @empty="reload()">Refresh</div>
<x-alert @error="handleError" />
<div @csrf-token="refresh"></div>

#Good

<!-- Longhand listener syntax never collides -->
<img :src="src" x-on:error="handleError">
<img :src="src" v-on:error="handleError">
<!-- The escape hands the attribute to the browser as written -->
<img :src="src" @@error="handleError">
<div @@empty="reload()">Refresh</div>
<!-- Unknown event names pass through Blade untouched -->
<button @click="open = true">Open</button>
<form @submit.prevent="save">...</form>
<!-- Blade directives used on purpose in attribute position -->
<x-input @checked($isChecked) />
<div @class(['p-4', 'font-bold' => $active])>...</div>

#Notes

  • The reported shape is a known directive name followed by an optional modifier, kebab suffix, or colon suffix, then optional HTML whitespace and = (for example @error.window = "..." and @csrf-token="..."). A bare @error with no = is not reported.
  • Checked on plain elements and component tags alike. Inside a component tag the same collision also stops the tag compiling as a component at all.
  • No auto-fix is available because the intended framework syntax cannot be inferred.

#Related Rules