一键导入
modern-livewire
How we write Livewire components - principles, patterns, and testing. Use when building or reviewing Livewire components, tests, or Blade views.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
How we write Livewire components - principles, patterns, and testing. Use when building or reviewing Livewire components, tests, or Blade views.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Teach a personal editor agent the user's own editing taste by reading the diff between an AI-drafted document and the user's hand-edit of it, encoding the lessons as procedures in the agent, and verifying with controlled re-runs. Use whenever the user has edited an AI-written document (README, docs, blog post) and wants their editor agent to learn from it, wants to build a personal editor agent from scratch, or says things like "teach my editor", "learn my house style", "train the humaniser on my edits", "make the editor agent edit more like me", or asks to compare what their editor agent would do against their own edit of the same document.
Generate a GitHub-style README.md for the current project by reading the actual codebase. Use when the user asks to "generate a readme", "write a readme", "document this project", "create a readme", or invokes /readme. Also callable from the github-create skill.
User's conventions and patterns for native macOS Swift apps. Use when working on a Swift or macOS app project
Streamlined workflow for implementing features with ait issue tracking. Handles: reading project context, checking outstanding work, planning new features, creating ait issues, and implementing with proper acceptance criteria checking. Use when starting a new session or beginning work on a feature.
One-stop review after feature work, or for a whole codebase. Runs deterministic checks (section ordering, arch conventions) free of charge, then fresh-eyes reviewer agents. Covers team conventions, test quality, complexity, security, and Livewire/Flux patterns - plus runtime checks (a11y, cold UX probe) when UI changed. Also has a quick mid-session sanity-check mode ("this seem ok?").
Turn a settled feature discussion or plan document into consultant-ready ait issues — an initiative/epic vision document plus implementation specs a fresh agent could pick up cold, tomorrow. Use towards the end of a feature conversation ("let's get this into ait"), or when converting a plan file. Also the knowledge base for the plan-to-ait agent. For driving the ait CLI itself, see the ait skill.
| name | modern-livewire |
| description | How we write Livewire components - principles, patterns, and testing. Use when building or reviewing Livewire components, tests, or Blade views. |
How we write Livewire components. Principles first, then patterns, then testing.
The right mindset prevents over-engineering before it starts.
Laravel validation works. Livewire form binding works. Eloquent relationships work. Don't rebuild what's already there.
Ask yourself: "Does the framework already handle this?"
// The framework handles this - you don't need to
public function save(): void
{
$this->validate(); // Laravel validates
$this->form->save(); // Done
}
Test what users see and what data changes. If a test would break when you refactor (without changing behaviour), it's testing implementation details.
// Test behaviour: what changed in the world?
it('creates a new role', function () {
Livewire::test(RolesList::class)
->set('roleName', 'Editor')
->call('saveRole');
expect(Role::where('name', 'Editor')->exists())->toBeTrue();
});
// Not implementation: what happened inside the component?
// (Don't test that $isModalOpen became false)
Don't hide problems with defensive guards and silent returns. On a trusted network with error monitoring, exceptions are helpful.
// Let it fail - this tells you something's wrong
public function updateLevel(int $skillId, SkillLevel $level): void
{
$this->user->skills()->updateExistingPivot($skillId, [
'level' => $level->value,
]);
}
Three similar lines are better than a premature abstraction. A button that's always visible is simpler than one that tracks whether it "should" be visible.
Trusted staff won't manipulate URLs. Students might - there's always one who'll try it on. Test accordingly.
Don't use boolean properties for modal state. Use the Flux facade.
use Flux\Flux;
// Open
Flux::modal('item-editor')->show();
// Close (typically after saving)
Flux::modal('item-editor')->close();
Cancel buttons close directly from Alpine - no server roundtrip:
<flux:button x-on:click="$flux.modal('item-editor').close()">Cancel</flux:button>
No $showModal properties. No cancelEdit() methods that just toggle a boolean.
For components with a handful of fields, use a single array:
public array $editing = [
'id' => null,
'name' => '',
'description' => '',
'cost' => '',
'is_active' => false,
];
public function openCreate(): void {
$this->reset('editing');
}
public function openEdit(int $id): void {
$model = Model::findOrFail($id);
$this->editing = $model->toArray();
}
<flux:input wire:model="editing.name" label="Name" />
Validation uses dot notation:
$this->validate([
'editing.name' => ['required', 'string', 'max:255'],
'editing.description' => ['nullable', 'string'],
]);
For components with many fields, extract to a Livewire Form object:
class CreatePostComponent extends Component
{
public PostForm $form;
public function save(): void
{
$post = $this->form->save();
Flux::toast('Post created');
$this->redirectRoute('posts.edit', $post);
}
}
$model = Model::findOrNew($this->editing['id']);
$model->fill($this->editing)->save();
Flux::toast('Saved.', variant: 'success');
fill() only uses $fillable attributes - non-fillable keys like id, created_at are automatically ignored.
If you need separate messages:
$action = $model->wasRecentlyCreated ? 'created' : 'updated';
Flux::toast("Item {$action}.", variant: 'success');
When form fields send empty strings but the database expects null:
// In your Model
protected function supplierId(): Attribute
{
return Attribute::make(
set: fn ($value) => $value ?: null,
);
}
Use null for "creating new", not -1 or other magic numbers:
public ?int $editingId = null;
if ($this->editingId === null) { /* create */ }
$this->dispatch('item-saved');
$this->dispatch('refresh')->to(OtherComponent::class);
Livewire defers wire:model by default. Use .live for real-time, .blur for on-blur:
| Modifier | Behaviour |
|---|---|
wire:model | Deferred (default) |
wire:model.live | Real-time sync |
wire:model.blur | Sync on blur |
// Unnecessary - (string) null already gives ''
$this->editing['supplier_id'] = (string) $model->supplier_id;
// Only use ?: null if the distinction actually matters
'cost' => $this->editing['cost'],
// GOOD: tests what changed in the world
it('creates a role', function () {
Livewire::test(RoleManager::class)
->call('openCreateModal')
->set('roleName', 'Editor')
->call('save')
->assertHasNoErrors();
expect(Role::where('name', 'Editor')->exists())->toBeTrue();
});
// BAD: tests component internals
->assertSet('isCreating', true)
->assertSet('showModal', false)
->call('resetModal')
// Test the full HTTP stack including middleware
$this->actingAs($user)->get(route('admin.items'))->assertForbidden();
// Not the component's internal auth check
// Livewire::test(AdminItems::class)->assertForbidden();
expect(Item::find($item->id))->toBeNull();
expect($item->users()->count())->toBe(0);
// Not raw database assertions
// $this->assertDatabaseMissing('items', ['id' => $item->id]);
it('validates required fields', function () {
Livewire::test(CreateUser::class)
->set('name', '')
->set('email', 'invalid')
->call('save')
->assertHasErrors(['name', 'email']);
expect(User::count())->toBe(0);
});
Before committing a test, ask: "If I refactored the component tomorrow without changing behaviour, would this test break?"
For more thorough side-by-side comparisons: