livewire-development — Claude Code skill for Livewire 4 components
Guides your agent through writing and debugging reactive Livewire 4 components: wire: directives, islands, async actions and tests.
npx skills add spatie/freek.dev --skill livewire-development -a claude-codeWhat livewire-development does
The skill activates when you create or edit a Livewire component, touch wire:model, wire:click or wire:loading, add live updates, or write component tests. It carries the component formats and the commands behind them: php artisan make:livewire create-post produces a single-file component, --mfc a multi-file one, --class the v3-style class. To switch formats, run php artisan livewire:convert create-post.
A separate section covers what changed in version 4. Full-page components now use Route::livewire(), config keys were renamed (layout → component_layout, lazy_placeholder → component_placeholder), and wire:model ignores child events by default — wire:model.deep restores the old behavior. Component tags must be closed or you get a syntax error. On a project still running version 3 these rules will clash with the code, so the skill tells you to check the app's existing conventions first.
Newer pieces include islands via @island for isolated updates, async actions with wire:click.async and the #[Async] attribute, deferred loading through defer and lazy.bundle. Directives wire:sort, wire:intersect and wire:ref are documented too. The practices section pushes wire:key in every @foreach loop, wire:model.live for instant updates since the default is debounced, and validation inside actions. Alpine.js ships with Livewire 4, so bundling it separately is a mistake.
What it helps with
- scaffold a Livewire component as single-file or multi-file
- convert a class-based component to the SFC format
- make a field update instantly with
wire:model.live - split a page into islands with isolated updates
- write a component test that asserts state after an action call
Example prompts for the agent
- create a Livewire component for a post creation form
- why doesn't
wire:modelupdate the counter in real time? - write a test for the Counter component that calls increment
How to install livewire-development
1Skills CLI (Vercel)
npx skills add spatie/freek.dev --skill livewire-development -a claude-code2Manually: put the files in place
~/.claude/skills/livewire-development/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/livewire-development/
1Skills CLI (Vercel)
npx skills add spatie/freek.dev --skill livewire-development -a codex2Manually: put the files in place
.agents/skills/livewire-development/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add spatie/freek.dev --skill livewire-development -a cursor2Manually: put the files in place
.cursor/skills/livewire-development/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install spatie/freek.dev livewire-development2Manually: put the files in place
.github/skills/livewire-development/SKILL.md1Skills CLI (Vercel)
npx skills add spatie/freek.dev --skill livewire-development -a gemini-cli2Platform CLI
gemini skills install https://github.com/spatie/freek.dev --path .claude/skills/livewire-developmentManually: put the files in place
.devin/skills/livewire-development/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add spatie/freek.dev --skill livewire-development -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about livewire-development
How do I install livewire-development in Claude?
Run npx skills add spatie/freek.dev --skill livewire-development -a claude-code. The skill lands in ~/.claude/skills/livewire-development/SKILL.md.
What does livewire-development do?
It gives the agent a Livewire 4 reference: component creation commands, wire: directives, islands, async actions, testing and common pitfalls.
How is Livewire 4 different from version 3?
Full-page components use Route::livewire(), config keys are renamed, wire:model ignores child events unless you add .deep, and wire:transition relies on the View Transitions API.
Source text
SKILL.md · MIT
---
name: livewire-development
description: >-
Develops reactive Livewire 4 components. Activates when creating, updating, or modifying
Livewire components; working with wire:model, wire:click, wire:loading, or any wire: directives;
adding real-time updates, loading states, or reactivity; debugging component behavior;
writing Livewire tests; or when the user mentions Livewire, component, counter, or reactive UI.
---
# 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.
## Basic Usage
### Creating Components
<code-snippet name="Component Creation Commands" lang="bash">
# Single-file component (default in v4)
{{ $assist->artisanCommand('make:livewire create-post') }}
# Multi-file component
{{ $assist->artisanCommand('make:livewire create-post --mfc') }}
# Class-based component (v3 style)
{{ $assist->artisanCommand('make:livewire create-post --class') }}
# With namespace
{{ $assist->artisanCommand('make:livewire Posts/CreatePost') }}
</code-snippet>
### 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
<code-snippet name="Single-File Component Example" lang="php">
<?php
use Livewire\Component;
new class extends Component {
public int $count = 0;
public function increment(): void
{
$this->count++;
}
}
?>
<div>
<button wire:click="increment">Count: @{{ $count }}</button>
</div>
</code-snippet>
## 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:model` now ignores child events by default (use `wire:model.deep` for old behavior); `wire:scroll` renamed to `wire:navigate:scroll`.
- Component tags must be properly closed; `wire:transition` now uses View Transitions API (modifiers removed).
- JavaScript: `$wire.$js('name', fn)` → `$wire.$js.name = fn`; `commit`/`request` hooks → `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.bundle` for 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-scroll` are available for use.
- `data-loading` attribute 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:key` in loops
- Use `wire:loading` for loading states
- Use `wire:model.live` for instant updates (default is debounced)
- Validate and authorize in actions (treat like HTTP requests)
## Configuration
- `smart_wire_keys` defaults to `true`; new configs: `component_locations`, `component_namespaces`, `make_command`, `csp_safe`.
## Alpine & JavaScript
- `wire:transition` uses browser View Transitions API; `$errors` and `$intercept` magic properties available.
- Non-blocking `wire:poll` and parallel `wire:model.live` updates improve performance.
For interceptors and hooks, see [reference/javascript-hooks.md](reference/javascript-hooks.md).
## Testing
<code-snippet name="Testing Example" lang="php">
Livewire::test(Counter::class)
->assertSet('count', 0)
->call('increment')
->assertSet('count', 1);
</code-snippet>
## Verification
1. Browser console: Check for JS errors
2. Network tab: Verify Livewire requests return 200
3. Ensure `wire:key` on all `@foreach` loops
## Common Pitfalls
- Missing `wire:key` in loops → unexpected re-rendering
- Expecting `wire:model` real-time → use `wire: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)
© spatie · MIT · spatie/freek.dev