agentsclimarketplace

Livewire component design

Skill kwhorne/elyra-skills/skills/livewire-component-design

Design Livewire components with clear boundaries - state ownership, props vs events, performance (network round-trips, lazy, computed), forms, and Alpine handoff. Use when creating or reviewing Livewire components, deciding how to split a page into components, debugging re-render or state-sync issues, or when a Livewire page feels slow.From its SKILL.md

Install
npx -y skills add kwhorne/elyra-skills --skill livewire-component-design

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 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.

What its file declares

Copied from the file, not written here

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

4.9 KB, ~1.1k tokens by cl100k_base, as published. Nobody here has run it

Livewire Component Design

Every Livewire interaction is a network round-trip carrying component state. Good component design minimizes what crosses the wire and who owns what.

When to use

  • Creating or splitting Livewire components ("one big component or three small?")
  • State desync, unexpected re-renders, or lost input
  • A Livewire page feels sluggish (too many/too heavy requests)
  • Deciding Livewire vs Alpine for an interaction

Principles

  • Components own state; children receive data and emit events. Two components owning the same state will disagree.
  • Alpine for the browser, Livewire for the server. Toggles, tabs, dropdowns = Alpine (zero round-trips). Anything touching data/authorization = Livewire.
  • Public properties are the payload — and the attack surface. Everything public crosses the wire every request and can be tampered with client-side. Keep it minimal, validate/authorize server-side always.
  • A component is a unit of update, not a unit of markup. Split where update frequency differs, not where the design grid does.

Process

1. Decide what's a component

  • Split when a region updates independently (search results vs filters vs cart badge)
  • Don't split static markup into components — Blade partials/components are free, Livewire components are not
  • Page-level component + small interactive islands beats a deep nesting tree

2. Assign state ownership

  • The owner mutates; everyone else reads
  • Parent → child: pass data as props (child re-renders when parent re-renders)
  • Child → parent: dispatch events ($this->dispatch('order-updated')), parent listens with #[On('order-updated')]
  • Cross-component shared state (cart count in navbar): events, or re-fetch in the listener — don't mirror writable copies

3. Keep the payload lean

  • Public properties: scalars, arrays, IDs. Prefer passing IDs + #[Computed] lookups over fat serialized models
  • #[Computed] for derived data — cached per request, doesn't ride the payload
  • #[Locked] on IDs and anything the client must not change (public #[Locked] int $orderId)
  • Never put secrets or other users' data in public properties — view-source shows them

4. Control the round-trips

PatternUse
wire:model.live.debounce.300msSearch-as-you-type (default wire:model is deferred — already cheap)
wire:model.blurValidate a field on blur
Alpine (x-show, x-data)Pure UI state — no server trip at all
#[Lazy] componentBelow-the-fold or expensive components; loads after page paint
Polling wire:poll.10sSparingly; prefer events/broadcasts for real-time
wire:loading / optimistic UIMask unavoidable latency

5. Forms

  • Form objects (UserForm extends Form) once a form exceeds a few fields — validation rules, state, and save() live together
  • Real-time validation: wire:model.blur + $this->validateOnly($field)
  • Always re-validate and re-authorize in the action method — client state is untrusted input

6. Test it

Livewire::test(OrderTable::class)
    ->set('search', 'foo')
    ->assertSee('foo-order')
    ->call('delete', $order->id)
    ->assertForbidden();          // authorization is testable — test it

Cover: initial render, each action, validation errors, authorization, emitted events (assertDispatched).

Output format

## Livewire design: <page/feature>

### Components
| Component | Owns | Receives | Emits | Lazy? |
|-----------|------|----------|-------|-------|
| …         | …    | …        | …     | …     |

### Wire-vs-Alpine
Alpine handles: … (zero round-trips). Livewire handles: …

### Payload review
Public props: … — locked: …, computed instead of serialized: …

### Round-trip budget
Interaction X: N requests → target M.

Anti-patterns

  • ❌ One God-component owning the whole page — every keystroke re-renders everything
  • ❌ Two components with writable copies of the same state, synced by hope
  • wire:model.live on every input out of habit (request per keystroke)
  • ❌ Fat models in public properties when the view needs three fields
  • ❌ Trusting a public property because "the UI doesn't let you change it" — #[Locked] or re-check
  • ❌ Livewire round-trip for a dropdown toggle Alpine does for free
  • ❌ Skipping authorization in actions because the button was hidden

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,629. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.