Livewire Development
When to Apply
Activate this skill when:
- Creating or modifying Livewire components
- Using wire: directives (model, click, loading, sort, intersect)
- Implementing islands or async actions
- Writing Livewire component tests
Documentation
Use search-docs for detailed Livewire 4 patterns and documentation.
Project Conventions
These conventions are specific to this project. When proposing changes to these conventions, update .ai/guidelines/project-guidelines.md.
Authorization
Use $this->authorize() with policies in Livewire action methods. Use #[CurrentUser] for injecting the authenticated user instead of the Auth facade.
php1// Good 2use Illuminate\Container\Attributes\CurrentUser; 3 4public function saveReport(#[CurrentUser] User $user): void 5{ 6 $this->authorize('create', [InjuryReport::class, $this->injury]); 7 // ... 8} 9 10// Bad 11public function saveReport(): void 12{ 13 if (Auth::id() !== $this->injury->user_id) { 14 return; 15 } 16 // ... 17}
Authorize at the action level (e.g. saveReport, deleteReport), not in mount(). This keeps authorization close to the mutation and leverages policies consistently.
Mount Methods
Omit mount() when it only assigns typed public properties — Livewire handles this automatically.
Basic Usage
Creating Components
<!-- Component Creation Commands -->bash1 2# Single-file component (default in v4) 3 4{{ $assist->artisanCommand('make:livewire create-post') }} 5 6# Multi-file component 7 8{{ $assist->artisanCommand('make:livewire create-post --mfc') }} 9 10# Class-based component (v3 style) 11 12{{ $assist->artisanCommand('make:livewire create-post --class') }} 13 14# With namespace 15 16{{ $assist->artisanCommand('make:livewire Posts/CreatePost') }}
Converting Between Formats
Use php artisan livewire:convert create-post to convert between single-file, multi-file, and class-based formats.
Component Format Reference
| Format | Flag | Structure |
|---|---|---|
| Single-file (SFC) | default | PHP + Blade in one file |
| Multi-file (MFC) | --mfc | Separate PHP class, Blade, JS, tests |
| Class-based | --class | Traditional v3 style class |
| View-based | ⚡ prefix | Blade-only with functional state |
Single-File Component Example
<!-- Single-File Component Example -->php1<?php 2use Livewire\Component; 3 4new class extends Component { 5 public int $count = 0; 6 7 public function increment(): void 8 { 9 $this->count++; 10 } 11} 12?> 13 14<div> 15 <button wire:click="increment">Count: @{{ $count }}</button> 16</div>
Livewire 4 Specifics
Key Changes From Livewire 3
These things changed in Livewire 4, but may not have been updated in this application. Verify this application's setup to ensure you follow existing conventions.
- Use
Route::livewire()for full-page components; config keys renamed:layout→component_layout,lazy_placeholder→component_placeholder. wire:modelnow ignores child events by default (usewire:model.deepfor old behavior);wire:scrollrenamed towire:navigate:scroll.- Component tags must be properly closed;
wire:transitionnow uses View Transitions API (modifiers removed). - JavaScript:
$wire.$js('name', fn)→$wire.$js.name = fn;commit/requesthooks →interceptMessage()/interceptRequest().
New Features
- Component formats: single-file (SFC), multi-file (MFC), view-based components.
- Islands (
@island) for isolated updates; async actions (wire:click.async,#[Async]) for parallel execution. - Deferred/bundled loading:
defer,lazy.bundlefor optimized component loading.
| Feature | Usage | Purpose |
|---|---|---|
| Islands | @island(name: 'stats') | Isolated update regions |
| Async | wire:click.async or #[Async] | Non-blocking actions |
| Deferred | defer attribute | Load after page render |
| Bundled | lazy.bundle | Load multiple together |
New Directives
wire:sort,wire:intersect,wire:ref,.renderless,.preserve-scrollare available for use.data-loadingattribute automatically added to elements triggering network requests.
| Directive | Purpose |
|---|---|
wire:sort | Drag-and-drop sorting |
wire:intersect | Viewport intersection detection |
wire:ref | Element references for JS |
.renderless | Component without rendering |
.preserve-scroll | Preserve scroll position |
Best Practices
- Always use
wire:keyin loops - Use
wire:loadingfor loading states - Use
wire:model.livefor instant updates (default is debounced) - Validate and authorize in actions (treat like HTTP requests)
Configuration
smart_wire_keysdefaults totrue; new configs:component_locations,component_namespaces,make_command,csp_safe.
Alpine & JavaScript
wire:transitionuses browser View Transitions API;$errorsand$interceptmagic properties available.- Non-blocking
wire:polland parallelwire:model.liveupdates improve performance.
For interceptors and hooks, see reference/javascript-hooks.md.
Testing
<!-- Testing Example -->php1Livewire::test(Counter::class) 2 ->assertSet('count', 0) 3 ->call('increment') 4 ->assertSet('count', 1);
Verification
- Browser console: Check for JS errors
- Network tab: Verify Livewire requests return 200
- Ensure
wire:keyon all@foreachloops
Common Pitfalls
- Missing
wire:keyin loops → unexpected re-rendering - Expecting
wire:modelreal-time → usewire:model.live - Unclosed component tags → syntax errors in v4
- Using deprecated config keys or JS hooks
- Including Alpine.js separately (already bundled in Livewire 4)