This guide provides instructions for AI assistants on how to write and edit Livewire v4 documentation.
All component examples use single-file anonymous class format:
<?php // resources/views/components/post/⚡create.blade.php
use Livewire\Component;
use App\Models\Post;
new class extends Component {
public $title = '';
public function save()
{
Post::create(['title' => $this->title]);
$this->redirect('/posts');
}
};
?>
<form wire:submit="save">
<input type="text" wire:model="title">
<button type="submit">Save</button>
</form><?php // resources/views/components/⚡name.blade.phpnew class extends Component (anonymous class)}; (or };?> if Blade follows in same block)render() method unless demonstrating lifecycle hooks#[Computed] properties for view data$this->// Simple components
<?php // resources/views/components/⚡todos.blade.php
// Nested/RESTful components
<?php // resources/views/components/post/⚡create.blade.php
<?php // resources/views/components/post/⚡edit.blade.php
// Pages
<?php // resources/views/pages/post/⚡create.blade.phpWhen to add file path comments:
post.create, post.edit, post.show, posts.indexpost.edit component..."todos, counter, dashboard, carttodos component..."Stay consistent: Use the same component name throughout a doc page unless genuinely different components.
Prefer separate blocks (better syntax highlighting):
<?php // resources/views/components/⚡todos.blade.php
use Livewire\Component;
new class extends Component { };<div>@foreach ($todos as $todo)...</div>Combine when needed:
<?php
new class extends Component { };
?>
<div>...</div>Do NOT use anonymous classes for:
App\Livewire\Forms\*)Tests\*)post.edit component" not "UpdatePost component"Attribute and directive documentation pages should include a Reference section at the bottom that provides technical details.
Include reference sections for:
#[Layout], #[Computed], #[Url])Omit reference sections for:
#[Locked], #[Reactive], #[Async])Place the reference section after "Learn more" or at the end of the page:
## Reference#[AttributeName(
type $param1,
type $param2 = default,)]
**`$param1`** (required)
- Description of what it does
**`$param2`** (optional)
- Description of what it does
- Default: `default_value`Parameter Signatures:
string, array, bool, int, ?string, mixed)Parameter Documentation:
Examples Subsection:
#[Computed])Anonymous Class Convention:
new keyword: new #[Layout('layouts::app')] class extends Component { }Avoid Legacy Patterns:
isolate: false deprecated in favor of bundle: true)When documenting wire:* directives, include a comprehensive Reference section at the bottom.
Before writing directive documentation:
grep -r "wire:directivename\." docs/.prevent, .stop, .self, .debounce, .throttle, .window, .document, .outside, .once, .passive, .capture, .camel, .dot)- .renderless - Skip re-rendering - .preserve-scroll - Maintain scroll position - .async - Parallel execution
## Referencewire:directivename="methodName" wire:directivename="methodName(param1, param2)"
### Modifiers
| Modifier | Description |
|----------|-------------|
| `.prevent` | Prevents default browser behavior |
| `.stop` | Stops event propagation |
| `.self` | Only triggers if event originated on this element |
| `.once` | Ensures listener is only called once |
| `.debounce` | Debounces handler by 250ms (use `.debounce.500ms` for custom duration) |
| `.throttle` | Throttles handler to every 250ms minimum (use `.throttle.500ms` for custom) |
| `.window` | Listens for event on the `window` object |
| `.document` | Listens for event on the `document` object |
| `.outside` | Only listens for clicks outside the element |
| `.passive` | Won't block scroll performance |
| `.capture` | Listens during the capturing phase |
| `.camel` | Converts event name to camel case |
| `.dot` | Converts event name to dot notation |
| `.renderless` | Skips re-rendering after action completes |
| `.preserve-scroll` | Maintains scroll position during updates |
| `.async` | Executes action in parallel instead of queued |Pest is the recommended testing framework for Livewire 4. All testing examples should use Pest syntax unless specifically demonstrating PHPUnit compatibility.
it('can create a post', function () {
expect(Post::count())->toBe(0);
Livewire::test('post.create')
->set('title', 'My new post')
->call('save');
expect(Post::count())->toBe(1);
});Only show PHPUnit when:
Link to Pest documentation where appropriate:
All layout file examples must match the official livewire.layout.stub format:
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ $title ?? config('app.name') }}</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
@livewireStyles
</head>
<body>
{{ $slot }}
@livewireScripts
</body>
</html>Key points:
config('app.name') not 'Page Title'@vite(['resources/css/app.css', 'resources/js/app.js']) directive@livewireStyles and @livewireScripts presentUse consistent terminology throughout the documentation:
Correct:
Incorrect:
Exception: The $commit() method name itself is correct (it's an alias for $refresh())
For single-request persistence (computed properties):
For cross-request persistence (session, database):
Single-file and multi-file components use bare <script> tags:
<script>
$wire.on('post-created', () => {
// Handle event
});
</script>Class-based components need the @script directive:
@script
<script>
$wire.on('post-created', () => {
// Handle event
});
</script>
@endscriptAlways include a warning in JavaScript documentation clarifying when @script is needed.
When showing component scripts:
$wire directly (it's available in the scope)this.$js.methodName for registering JavaScript methods@script wrapper in single-file component examplesWhen documenting Blade directives, the @ symbol needs special handling:
In regular prose within backticks:
The `@persist` directive allows you to...Works fine - no escaping needed.
In tip/warning/info blocks within backticks:
> [!tip]
> The `@@persist` directive requires...Use @@ (double at sign) inside tip/warning/info blocks.
In code blocks:
@persist('player')
<audio controls></audio>
@endpersistUse single @ in code blocks - they render literally.
Use callouts to highlight important information:
> [!tip] Smoke tests provide huge value
> Tests like these provide enormous value as they require very little maintenance.> [!warning] Must teleport outside the component
> Livewire only supports teleporting HTML outside your components.> [!info] When to use browser tests
> Use browser tests for critical user flows and complex interactions.Guidelines:
Link to related documentation generously:
Format:
For more information, see [URL feature](/docs/4.x/url).
Learn more about [lazy loading](/docs/4.x/lazy).Guidelines:
/docs/4.x/feature-name#[Computed] or @persistfile.php:123 for line numbersMany documentation pages benefit from a "See also" section at the bottom that guides readers to related topics. This helps users discover connected features and understand how different parts of Livewire work together.
Include "See also" sections for:
Skip "See also" sections for:
Place the section at the very bottom of the page:
## See also
- **[Page Title](/docs/4.x/url)** — Brief description of how it relates
- **[Another Page](/docs/4.x/url)** — Why this is relevant to the current topic
- **[Third Page](/docs/4.x/url)** — What the user will learn thereFor foundational pages (e.g., Properties):
## See also
- **[Forms](/docs/4.x/forms)** — Bind properties to form inputs with wire:model
- **[Computed Properties](/docs/4.x/computed-properties)** — Create derived values with automatic memoization
- **[Validation](/docs/4.x/validation)** — Validate property values before persisting
- **[Locked Attribute](/docs/4.x/attribute-locked)** — Prevent properties from being manipulated client-sideFor feature pages (e.g., Islands):
## See also
- **[Nesting](/docs/4.x/nesting)** — Alternative approach using child components
- **[Lazy Loading](/docs/4.x/lazy)** — Defer loading of expensive content
- **[Computed Properties](/docs/4.x/computed-properties)** — Optimize island performance with memoizationFor directive pages (e.g., wire:model):
## See also
- **[Forms](/docs/4.x/forms)** — Complete guide to building forms with Livewire
- **[Properties](/docs/4.x/properties)** — Understand data binding and property management
- **[Validation](/docs/4.x/validation)** — Validate bound properties in real-timePrioritize these relationship types: 1. Prerequisites - Concepts users should understand first 2. Common combinations - Features often used together 3. Alternatives - Different approaches to similar problems 4. Deep dives - More detailed coverage of mentioned topics 5. Related attributes/directives - PHP attributes or Blade directives for the feature
When adding a "See also" section:
Structure learning-focused pages with clear progression:
Move reference material to the bottom. Users learning a feature don't need exhaustive API details up front.
Structure API-focused pages clearly:
For complex features, include a "Common Patterns" section with 3-5 real-world examples:
## Common patterns
### Integrating third-party libraries
[Practical example with real library]
### Syncing with localStorage
[Actual code that works]Guidelines:
For features with performance/debugging considerations, add a "Best Practices" section:
## Best practices
### When to use X vs Y
**Use X when:**
- Specific scenario
- Another scenario
**Use Y when:**
- Different scenario
### Performance considerations
- Concrete tip
- Another tip with exampleGuidelines:
Avoid these patterns:
❌ Using emojis (unless user explicitly requests) ❌ Superlatives ("amazing", "powerful", "beautiful") ❌ Marketing language ("Livewire makes it easy!") ❌ Vague examples (// Do something here) ❌ Hypothetical scenarios ("Imagine you want to...") ❌ Second-person tutorial style ("You can now...") ❌ Starting sentences with "Now," or "Next," ❌ Long paragraphs (break up after 3-4 lines) ❌ Code without context (always show usage) ❌ Duplicate examples (each example should teach something new)
Prefer these patterns:
✅ Clear, factual statements ✅ Active voice ("Livewire provides...") ✅ Concrete, working examples ✅ Progressive disclosure (simple → complex) ✅ Consistent component names within a page ✅ Short paragraphs and clear headings ✅ Real-world use cases ✅ Cross-references to related features
wire: directives over x- when both work<livewire:post.create />data-current: not `data- for data attribute variants (cleaner, no brackets needed)data-loading: not `data- for data attribute variantsdata-current:font-bold, data-loading:opacity-50$wire for Livewire, Alpine.js for AlpineEvery code example should have a purpose:
Good:
// Shows how to pass parameters to mount()
Livewire::test('post.edit', ['post' => $post])
->assertSet('title', 'Existing post title');Bad:
// Generic example with no context
Livewire::test('some.component')
->assertSet('property', 'value');Guidelines:
Balance completeness with scanability:
Too sparse:
## Feature
This feature does something.
[single basic example]Too dense:
## Feature
[wall of text explaining every edge case]
[10 similar examples]
[exhaustive parameter listing up front]Just right:
## Feature
[What it is - 1 sentence]
[Basic example]
[2-3 common use cases with examples]
[Important constraints with tip box]
## Reference
[Detailed parameters for advanced users]When improving existing docs:
Before considering documentation complete:
@script wrapper for single-file components