Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Build Laravel 10+ apps with Eloquent, Sanctum auth, Horizon queues, Livewire, and RESTful API resources.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/livewire.md
1# Livewire Components23## Component Patterns45```php6namespace App\Http\Livewire;78use Livewire\Component;9use Livewire\WithPagination;10use Livewire\WithFileUploads;11use App\Models\Post;1213class PostList extends Component14{15use WithPagination, WithFileUploads;1617public string $search = '';18public string $sortBy = 'created_at';19public string $sortDirection = 'desc';20public ?int $categoryId = null;2122protected $queryString = [23'search' => ['except' => ''],24'sortBy' => ['except' => 'created_at'],25'categoryId' => ['except' => null],26];2728public function updatingSearch(): void29{30$this->resetPage();31}3233public function sortBy(string $field): void34{35if ($this->sortBy === $field) {36$this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';37} else {38$this->sortBy = $field;39$this->sortDirection = 'asc';40}41}4243public function render()44{45return view('livewire.post-list', [46'posts' => Post::query()47->when($this->search, fn($q) => $q->where('title', 'like', "%{$this->search}%"))48->when($this->categoryId, fn($q) => $q->where('category_id', $this->categoryId))49->orderBy($this->sortBy, $this->sortDirection)50->paginate(10),51]);52}53}54```5556## Blade Template5758```blade59<div>60{{-- Search --}}61<input62type="text"63wire:model.debounce.300ms="search"64placeholder="Search posts..."65class="form-input"66>6768{{-- Filter by category --}}69<select wire:model="categoryId">70<option value="">All Categories</option>71@foreach($categories as $category)72<option value="{{ $category->id }}">{{ $category->name }}</option>73@endforeach74</select>7576{{-- Sortable table --}}77<table>78<thead>79<tr>80<th wire:click="sortBy('title')" style="cursor: pointer">81Title82@if($sortBy === 'title')83<span>{{ $sortDirection === 'asc' ? '↑' : '↓' }}</span>84@endif85</th>86<th wire:click="sortBy('created_at')" style="cursor: pointer">87Date88@if($sortBy === 'created_at')89<span>{{ $sortDirection === 'asc' ? '↑' : '↓' }}</span>90@endif91</th>92</tr>93</thead>94<tbody>95@foreach($posts as $post)96<tr>97<td>{{ $post->title }}</td>98<td>{{ $post->created_at->diffForHumans() }}</td>99</tr>100@endforeach101</tbody>102</table>103104{{-- Pagination --}}105{{ $posts->links() }}106107{{-- Loading states --}}108<div wire:loading wire:target="search">109Searching...110</div>111</div>112```113114## Form Component115116```php117namespace App\Http\Livewire;118119use Livewire\Component;120use App\Models\Post;121122class PostForm extends Component123{124public ?Post $post = null;125public string $title = '';126public string $content = '';127public array $tags = [];128public $image;129130protected function rules(): array131{132return [133'title' => 'required|min:3|max:255',134'content' => 'required|min:10',135'tags' => 'array|max:5',136'tags.*' => 'exists:tags,id',137'image' => 'nullable|image|max:2048',138];139}140141public function mount(?Post $post = null): void142{143if ($post) {144$this->post = $post;145$this->title = $post->title;146$this->content = $post->content;147$this->tags = $post->tags->pluck('id')->toArray();148}149}150151public function updated($propertyName): void152{153$this->validateOnly($propertyName);154}155156public function save(): void157{158$validated = $this->validate();159160if ($this->post) {161$this->post->update($validated);162$message = 'Post updated successfully!';163} else {164$this->post = Post::create($validated);165$message = 'Post created successfully!';166}167168if ($this->image) {169$this->post->update([170'image_path' => $this->image->store('posts', 'public'),171]);172}173174$this->post->tags()->sync($this->tags);175176session()->flash('message', $message);177$this->redirect(route('posts.show', $this->post));178}179180public function render()181{182return view('livewire.post-form');183}184}185```186187## Form Template188189```blade190<form wire:submit.prevent="save">191{{-- Title --}}192<div>193<label for="title">Title</label>194<input195type="text"196wire:model.defer="title"197id="title"198class="@error('title') border-red-500 @enderror"199>200@error('title')201<span class="text-red-500">{{ $message }}</span>202@enderror203</div>204205{{-- Content --}}206<div>207<label for="content">Content</label>208<textarea209wire:model.defer="content"210id="content"211class="@error('content') border-red-500 @enderror"212></textarea>213@error('content')214<span class="text-red-500">{{ $message }}</span>215@enderror216</div>217218{{-- Tags --}}219<div>220<label>Tags</label>221@foreach($availableTags as $tag)222<label>223<input224type="checkbox"225wire:model="tags"226value="{{ $tag->id }}"227>228{{ $tag->name }}229</label>230@endforeach231@error('tags')232<span class="text-red-500">{{ $message }}</span>233@enderror234</div>235236{{-- File Upload --}}237<div>238<label>Image</label>239<input type="file" wire:model="image">240241@error('image')242<span class="text-red-500">{{ $message }}</span>243@enderror244245{{-- Upload progress --}}246<div wire:loading wire:target="image">247Uploading...248</div>249250{{-- Preview --}}251@if ($image)252<img src="{{ $image->temporaryUrl() }}" alt="Preview">253@endif254</div>255256{{-- Submit --}}257<button type="submit" wire:loading.attr="disabled">258<span wire:loading.remove>Save</span>259<span wire:loading>Saving...</span>260</button>261</form>262263@if (session()->has('message'))264<div class="alert alert-success">265{{ session('message') }}266</div>267@endif268```269270## Real-time Validation271272```php273class PostForm extends Component274{275public string $title = '';276277protected $rules = [278'title' => 'required|min:3|unique:posts,title',279];280281// Real-time validation282public function updated($propertyName): void283{284$this->validateOnly($propertyName);285}286287// Custom validation messages288protected $messages = [289'title.required' => 'The post title is required.',290'title.min' => 'The title must be at least 3 characters.',291'title.unique' => 'This title is already taken.',292];293294// Custom attribute names295protected $validationAttributes = [296'title' => 'post title',297];298}299```300301## Events302303```php304// Emit event305class PostList extends Component306{307public function deletePost($postId): void308{309Post::find($postId)->delete();310311$this->emit('postDeleted', $postId);312}313}314315// Listen to event316class PostStats extends Component317{318protected $listeners = ['postDeleted' => 'updateStats'];319320public function updateStats($postId): void321{322// Update statistics323}324}325326// Emit to specific component327$this->emitTo('post-stats', 'refresh');328329// Emit to parent/children330$this->emitUp('saved');331$this->emitSelf('refresh');332333// Browser events334$this->dispatchBrowserEvent('post-saved', ['id' => $post->id]);335```336337## Listen to Browser Events338339```blade340<div341x-data342@post-saved.window="alert('Post saved!')"343>344<!-- content -->345</div>346347<script>348window.addEventListener('post-saved', event => {349console.log('Post ID:', event.detail.id);350});351</script>352```353354## Polling355356```blade357{{-- Poll every 2 seconds --}}358<div wire:poll.2s>359Current time: {{ now() }}360</div>361362{{-- Poll specific action --}}363<div wire:poll.5s="checkStatus">364Status: {{ $status }}365</div>366367{{-- Keep polling until condition --}}368<div wire:poll.keep-alive.2s>369<!-- content -->370</div>371```372373## Loading States374375```blade376{{-- Basic loading state --}}377<div wire:loading>378Loading...379</div>380381{{-- Target specific action --}}382<div wire:loading wire:target="save">383Saving...384</div>385386{{-- Hide element while loading --}}387<div wire:loading.remove>388Content (hidden during load)389</div>390391{{-- Delay loading indicator --}}392<div wire:loading.delay>393This appears after 200ms394</div>395396{{-- Custom delay --}}397<div wire:loading.delay.longest>398This appears after 1s399</div>400401{{-- Loading classes --}}402<button403wire:click="save"404wire:loading.class="opacity-50"405wire:loading.class.remove="bg-blue-500"406>407Save408</button>409410{{-- Loading attributes --}}411<button412wire:click="save"413wire:loading.attr="disabled"414>415Save416</button>417```418419## Traits420421```php422// Pagination423use Livewire\WithPagination;424425class PostList extends Component426{427use WithPagination;428429public function render()430{431return view('livewire.post-list', [432'posts' => Post::paginate(10),433]);434}435}436437// File uploads438use Livewire\WithFileUploads;439440class UploadPhoto extends Component441{442use WithFileUploads;443444public $photo;445446public function save(): void447{448$this->validate([449'photo' => 'image|max:1024',450]);451452$this->photo->store('photos');453}454}455```456457## Authorization458459```php460class PostForm extends Component461{462public Post $post;463464public function mount(Post $post): void465{466$this->authorize('update', $post);467$this->post = $post;468}469470public function save(): void471{472$this->authorize('update', $this->post);473// Save logic474}475}476```477478## Performance Tips4794801. **Use wire:model.defer** - Batch updates on form submit4812. **Lazy load components** - Use wire:init for heavy operations4823. **Cache computed properties** - Use #[Computed] attribute4834. **Disable polling when hidden** - Use wire:poll.visible4845. **Optimize queries** - Eager load relationships4856. **Use wire:key** - Prevent re-rendering entire lists4867. **Debounce input** - Use wire:model.debounce4878. **Use pagination** - Don't load all records at once488489```php490use Livewire\Attributes\Computed;491492class PostList extends Component493{494#[Computed]495public function posts()496{497return Post::with('user')->paginate(10);498}499500public function render()501{502return view('livewire.post-list');503}504}505```506507```blade508{{-- Access computed property --}}509@foreach($this->posts as $post)510<!-- content -->511@endforeach512```513