Livewire audit
Detect common Livewire/Volt problems - unserializable properties, missing wire:key, orphaned events, re-render issues.From its SKILL.md
npx -y skills add edulazaro/laraclaude --skill livewire-auditAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 12 stars12 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
9.8 KB, as published. Nobody here has run it
Subcommands
| Subcommand | Description |
|---|---|
report | Scan all Volt components and report issues without making changes |
fix | Scan all Volt components, report issues, and auto-fix what is safe to fix |
fix --dry-run | Show what fixes would be applied without writing changes |
<file-path> | Scan a single file and report issues |
fix <file-path> | Scan a single file and auto-fix issues |
Overview
This skill audits Livewire/Volt components for common bugs, anti-patterns, and runtime errors. It targets issues that cause serialization failures, DOM diffing bugs, broken event wiring, and silent rendering problems.
Instructions
Step 1: Determine scope
Parse the argument to decide what to scan:
- No argument or
report: Scan all.blade.phpfiles underresources/views/livewire/that contain Volt component signatures (new class extends Component). fix(no path): Same scope asreportbut apply fixes.fix --dry-run: Same scope asfixbut only show proposed changes.<file-path>: Scan only the specified file.fix <file-path>: Scan and fix only the specified file.
Use Glob to find files:
resources/views/livewire/**/*.blade.php
Filter to only files containing Volt component PHP blocks (look for new class extends Component).
Step 2: Run each detection rule
For every file in scope, check the following rules in order:
Rule 1: Unserializable public properties
Livewire can only serialize: primitives (string, int, float, bool, null, array), Eloquent Models, Eloquent Collections, Carbon instances, Stringable, and Enums.
Pattern to detect: Public properties with type hints or assignments that are objects NOT in the safe list.
Look for:
public SomeClass $propwhereSomeClassis not a Model, Collection, Carbon, BackedEnum, or Stringable$this->prop = new SomeObject()in mount/methods where$propis public andSomeObjectis not safe- Public properties assigned closures or anonymous functions
Report format:
[UNSERIALIZABLE] file.blade.php:42
Public property `$fieldConfig` has type `FieldConfig` which cannot be serialized by Livewire.
Suggestion: Make it protected and use a getter method, or convert to array.
Fix: No auto-fix (requires manual refactoring). Report only.
Rule 2: Missing wire:key in @foreach loops rendering Livewire components
Pattern to detect: @foreach loops that contain <livewire: or @livewire( tags without a wire:key or :wire:key attribute.
Look for blocks like:
@foreach($items as $item)
<livewire:some-component :model="$item" />
@endforeach
Where the <livewire: tag does NOT have wire:key="..." or :wire:key="...".
Also check for @foreach loops containing <div> or other elements wrapping Livewire components without wire:key on either the wrapper or the component.
Report format:
[MISSING-WIRE-KEY] file.blade.php:87
@foreach loop renders <livewire:card> without wire:key.
This causes DOM diffing bugs when items are reordered or removed.
Fix: Add wire:key="{{ $loop->index }}" to the Livewire component tag. Prefer using a unique model identifier if available (e.g., wire:key="component-{{ $item->id }}").
Rule 3: wire:model referencing non-existent public properties
Pattern to detect: wire:model="fieldName" or wire:model.live="fieldName" in the blade section where fieldName (or the root property if dot notation) is not declared as a public property in the PHP block.
Parse the PHP class block to extract all public property names. Then scan the blade template for wire:model bindings and check each root property name exists.
Handle dot notation: wire:model="data.name" means the root property is $data.
Report format:
[MISSING-PROPERTY] file.blade.php:123
wire:model="clientName" but no public property `$clientName` found in component.
Fix: No auto-fix (the developer must decide the property type and default). Report only.
Rule 4: Orphaned dispatched events
Pattern to detect: $this->dispatch('event-name') or $dispatch('event-name') where no component in the project has a matching #[On('event-name')] listener.
Exclude well-known framework events: close-modal, open-modal, close-drawer, open-drawer, notify, toast, refresh.
Search across ALL Volt components (not just the current file) for #[On('event-name')] attributes.
Report format:
[ORPHANED-EVENT] file.blade.php:56
Dispatches 'property-updated' but no component has #[On('property-updated')] listener.
Fix: No auto-fix. Report only.
Rule 5: Missing wire:ignore for JS-manipulated DOM inside Livewire components
Pattern to detect: Elements with id attributes that are referenced by JavaScript (document.getElementById, document.querySelector('#id')) inside or alongside the component, where the parent does not have wire:ignore.
Also detect: Elements inside <x-modal> or drawer components that are populated by JavaScript event listeners (look for addEventListener patterns that set textContent, innerHTML, or value).
Report format:
[MISSING-WIRE-IGNORE] file.blade.php:145
Element #eventTitle is updated by JavaScript but not inside a wire:ignore block.
Livewire will reset this content on re-render.
Fix: Wrap the affected elements in a <div wire:ignore> container.
Rule 6: wire:model.live without debounce on potentially expensive operations
Pattern to detect: wire:model.live="prop" where the component has methods triggered by that property update (e.g., updated hooks, computed properties that query the database, or the property is used in a query inside render()).
Also flag: multiple wire:model.live bindings in the same form (each keystroke on any field = full server roundtrip).
Report format:
[MISSING-DEBOUNCE] file.blade.php:78
wire:model.live="search" triggers server request on every keystroke.
Consider wire:model.live.debounce.300ms="search".
Fix: Replace wire:model.live="prop" with wire:model.live.debounce.300ms="prop".
Rule 7: Missing wire:navigate on internal links
Pattern to detect: <a href="..."> tags pointing to internal routes (containing route('...') or relative paths starting with /) that do NOT have wire:navigate attribute.
Exclude: links with target="_blank", links to external URLs, anchor links (#), links to file downloads, links already having wire:navigate.
Only flag this in files that use an SPA layout (check if the layout extends a layout with @livewireScripts or uses wire:navigate elsewhere).
Report format:
[MISSING-WIRE-NAVIGATE] file.blade.php:34
Internal link <a href="{{ route('properties.index') }}"> missing wire:navigate.
Fix: Add wire:navigate attribute to the <a> tag.
Rule 8: Public Eloquent Collections
Pattern to detect: Public properties typed as \Illuminate\Database\Eloquent\Collection or \Illuminate\Support\Collection, or assigned from ->get(), ->all(), or Collection::make().
Large collections as public properties increase payload size on every Livewire request.
Report format:
[COLLECTION-PROPERTY] file.blade.php:15
Public property `$users` is an Eloquent Collection. Consider using ->toArray(),
a computed property, or pagination to reduce payload size.
Fix: No auto-fix. Report only with suggestion.
Rule 9: Redirect with navigate:true to external URLs
Pattern to detect: $this->redirect('http...', navigate: true) or $this->redirectRoute('...', navigate: true) where the URL is an external domain.
Report format:
[BAD-REDIRECT] file.blade.php:67
$this->redirect() with navigate:true to external URL will fail.
Remove navigate:true for external redirects.
Fix: Remove navigate: true from the redirect call.
Step 3: Generate report
Group findings by file, then by severity:
Severity levels:
- ERROR: Will cause runtime failures (unserializable, bad redirect, missing property)
- WARNING: Will cause bugs or unexpected behavior (missing wire:key, orphaned events, missing wire:ignore)
- INFO: Performance or best-practice suggestions (debounce, wire:navigate, collections)
Output format:
## Livewire Audit Report
### resources/views/livewire/calendar.blade.php
- [ERROR] Line 42: Unserializable public property `$fieldConfig` (FieldConfig)
- [WARNING] Line 87: @foreach renders <livewire:card> without wire:key
- [INFO] Line 78: wire:model.live="search" should use debounce
### resources/views/livewire/properties/index.blade.php
- [WARNING] Line 34: Internal link missing wire:navigate
---
**Summary**: 2 errors, 2 warnings, 1 info across 2 files.
Step 4: Apply fixes (if fix mode)
For fix or fix <file-path>:
- Show each proposed change with before/after context
- Apply the change using Edit tool
- Report what was changed
For fix --dry-run:
- Show each proposed change with before/after context
- Do NOT apply changes
- Report what would be changed
Safe auto-fixes (applied automatically):
- Add
wire:keyto Livewire components in loops - Add
debounce.300mstowire:model.live - Add
wire:navigateto internal links - Remove
navigate: truefrom external redirects - Wrap JS-manipulated elements in
wire:ignore
Unsafe fixes (report only, never auto-fix):
- Unserializable properties (requires refactoring)
- Missing public properties for wire:model (developer decision)
- Orphaned events (may be cross-component or in JS)
- Collection properties (multiple valid solutions)