Skip to content

Commit 58d9e1e

Browse files
authored
Merge pull request #28 from laravel/ai-73-improve-livewire-guidelines
Improve Livewire guidelines
2 parents be37f15 + fc193a2 commit 58d9e1e

File tree

11 files changed

+110
-53
lines changed

11 files changed

+110
-53
lines changed

.ai/livewire/2/core.blade.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
- `wire:model` is live by default.
2+
- **Namespace**: Components typically exist in `App\Http\Livewire`.
3+
- **Events**: Use `emit()`, `emitTo()`, `emitSelf()` and `dispatchBrowserEvent()` for events.
4+
- Alpine is included separately to Livewire.
5+
- You can listen for `livewire:load` to hook into Livewire initialization, and `Livewire.onPageExpired` for when the page expires:
6+
@verbatim
7+
<code-snippet name="livewire:load example" lang="js">
8+
document.addEventListener('livewire:load', function () {
9+
Livewire.onPageExpired(() => {
10+
alert('Your session expired');
11+
});
12+
13+
Livewire.onError(status => console.error(status));
14+
});
15+
</code-snippet>
16+
@endverbatim

.ai/livewire/3/core.blade.php

Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,31 @@
1-
2-
#### Key Changes from Livewire 2
3-
4-
- **Namespace**: Components now use `App\Livewire` (not `App\Http\Livewire`)
5-
- **Events**: Use `$this->dispatch()` (not `emit` or `dispatchBrowserEvent`)
6-
- **Layout path**: `components.layouts.app` (not `layouts.app`)
7-
- **Deferred by default**: Use `wire:model.live` for real-time updates
8-
- **Alpine included**: Don't manually include Alpine.js
9-
10-
#### Livewire Best Practices
11-
12-
- **Single root element** in Blade components
13-
- **Add wire:key** in loops:
14-
1+
## Key Changes from Livewire 2
2+
- These changed in Livewire 2, but may not have been updated in this project. Verify this project's setup to ensure you conform with project conventions.
3+
- **Wire:model**: Use `wire:model.live` for real-time updates, `wire:model` is now deferred by default.
4+
- **Namespace**: Components now use `App\Livewire` (not `App\Http\Livewire`).
5+
- **Events**: Use `$this->dispatch()` (not `emit` or `dispatchBrowserEvent`).
6+
- **Layout path**: `components.layouts.app` (not `layouts.app`).
7+
8+
## New directives
9+
- `wire:show`, `wire:transition`, `wire:cloak, `wire:offline`, `wire:target` are available for use. Use the docs to find usages.
10+
11+
## Alpine
12+
- Alpine is now included with Livewire, don't manually include Alpine.js.
13+
- Plugins built in to Alpine: persist, intersect, collapse, and focus.
14+
15+
## Lifecycle hooks
16+
- You can listen for `livewire:init` to hook into Livewire initialization, and `fail.status === 419` for the page expiring:
1517
@verbatim
16-
```blade
17-
@foreach ($items as $item)
18-
<div wire:key="item-{{ $item->id }}">
19-
{{ $item->name }}
20-
</div>
21-
@endforeach
22-
```
18+
<code-snippet name="livewire:load example" lang="js">
19+
document.addEventListener('livewire:init', function () {
20+
Livewire.hook('request', ({ fail }) => {
21+
if (fail && fail.status === 419) {
22+
alert('Your session expired');
23+
}
24+
});
25+
26+
Livewire.hook('message.failed', (message, component) => {
27+
console.error(message);
28+
});
29+
});
30+
</code-snippet>
2331
@endverbatim
24-
25-
- **Use attributes** for event listeners:
26-
27-
```php
28-
#[On('todo-created')]
29-
public function refreshList()
30-
{
31-
// ...
32-
}
33-
```
34-
35-
- **Loading states**: Use `wire:loading` and `wire:dirty`
36-
- **Confirmations**: Use `wire:confirm="Are you sure?"`

.ai/livewire/core.blade.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,44 @@
11
## Livewire Core
2+
- Use the `search-docs` tool to find exact version specific documentation for how to write Livewire & Livewire tests.
3+
- Use the `php artisan make:livewire [Posts\\CreatePost]` artisan command to create new components
4+
- State should live on the server, with the UI reflecting it.
5+
- All Livewire requests hit the Laravel backend, they're like regular HTTP requests. Always validate form data, and run authorization checks in Livewire actions.
6+
7+
## Livewire Best Practices
8+
- Livewire components require a single root element.
9+
- Use `wire:loading` and `wire:dirty` for delightful loading states.
10+
- Add `wire:key` in loops:
11+
@verbatim
12+
```blade
13+
@foreach ($items as $item)
14+
<div wire:key="item-{{ $item->id }}">
15+
{{ $item->name }}
16+
</div>
17+
@endforeach
18+
```
19+
@endverbatim
20+
- Prefer lifecycle hooks like `mount()`, `updatedFoo()`) for initialization and reactive side effects:
21+
@verbatim
22+
<code-snippet name="Lifecycle hook examples" lang="php">
23+
public function mount(User $user) { $this->user = $user; }
24+
public function updatedSearch() { $this->resetPage(); }
25+
</code-snippet>
26+
@endverbatim
27+
28+
## Testing Livewire
29+
@verbatim
30+
<code-snippet name="Example Livewire component test" lang="php">
31+
Livewire::test(Counter::class)
32+
->assertSet('count', 0)
33+
->call('increment')
34+
->assertSet('count', 1)
35+
->assertSee(1)
36+
->assertStatus(200);
37+
</code-snippet>
38+
@endverbatim
39+
@verbatim
40+
<code-snippet name="Testing a Livewire component exists within a page" lang="php">
41+
$this->get('/posts/create')
42+
->assertSeeLivewire(CreatePost::class);
43+
</code-snippet>
44+
@endverbatim

.ai/tailwindcss/3/core.blade.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
- Always use Tailwind CSS v3, verify you're using only supported classes.
2-
- Use the `search-docs` tool to find exactly what's supported in this project's Tailwind setup.

.ai/tailwindcss/4/.gitkeep

Whitespace-only changes.

.ai/tailwindcss/4/core.blade.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
- Always use Tailwind CSS v4, do not use the deprecated utilities.
2-
- Use the `search-docs` tool to find exactly what's supported in this project's Tailwind setup.
32
- In Tailwind v4 you import Tailwind using a regular CSS `@import` statement, not using the `@tailwind` directives used in v3:
43
@verbatim
54
<code-snippet name="Tailwind v4 import tailwind diff" lang="diff"

.ai/tailwindcss/core.blade.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
- Use Tailwind CSS classes to style HTML, check and use existing tailwind conventions within the project before writing your own.
22
- Offer to extract repeated patterns into components that match the project's conventions (i.e. Blade, JSX, Vue, etc..)
33
- Think through class placement, order, priority, and defaults - remove redundant classes, add classes to parent or child carefully to limit repetition, group elements logically
4+
- Use the `search-docs` tool to find exactly what's supported in this project's Tailwind setup.
45

56
## Spacing
67
- Use gap utilities for spacing, don't use margins

.ai/volt/core.blade.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
- This project uses Livewire Volt for interactivity within its pages. New pages requiring interactivity must also use Livewire Volt. There is documentation available for it.
2-
- Volt is an elegantly crafted **functional** API for Livewire that supports single-file components, allowing a component's PHP logic and Blade templates to coexist in the same file
2+
- Make new Volt components using `php artisan make:volt [name] [--test] [--pest]`
3+
- Volt is a **functional** API for Livewire that supports single-file components, allowing a component's PHP logic and Blade templates to coexist in the same file
34
- **Single-File Components**: Livewire Volt allows PHP logic and Blade templates in one file. Components use the `@volt` directive.
45
- You must check existing Volt components to find out if they're functional or class based. If you can't detect that, ask the user which they prefer before writing a Volt component.
56

@@ -107,11 +108,10 @@ public function increment()
107108
$delete = fn(Product $product) => $product->delete();
108109
?>
109110

110-
<!-- UI here -->
111+
<!-- HTML here -->
111112
@endvolt
112113
</code-snippet>
113114
@endverbatim
114-
115115
@verbatim
116116
<code-snippet name="Real-time search with Volt" lang="php">
117117
<flux:input
@@ -120,8 +120,6 @@ public function increment()
120120
/>
121121
</code-snippet>
122122
@endverbatim
123-
124-
125123
@verbatim
126124
<code-snippet name="Loading states with Volt" lang="php">
127125
<flux:button wire:click="save" wire:loading.attr="disabled">

src/Mcp/Tools/SearchDocs.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,20 @@ public function __construct(protected Roster $roster)
2222

2323
public function description(): string
2424
{
25-
return 'Search for up-to-date version-specific documentation related to this project and its packages. This tool will search Laravel hosted documentation based on the packages installed and is perfect for all Laravel related packages. Laravel, inertia, pest, livewire, filament, nova, nightwatch, and more.'.PHP_EOL.'You must use this tool to search for Laravel-ecosystem docs before using other approaches.';
25+
return 'Search for up-to-date version-specific documentation related to this project and its packages. This tool will search Laravel hosted documentation based on the packages installed and is perfect for all Laravel related packages. Laravel, inertia, pest, livewire, filament, nova, nightwatch, and more.'.PHP_EOL.'You must use this tool to search for Laravel-ecosystem docs before using other approaches. The results provided are for this project\'s package version and does not cover all versions of the package.';
2626
}
2727

2828
public function schema(ToolInputSchema $schema): ToolInputSchema
2929
{
3030
return $schema
31-
->string('queries')
32-
->description('### separated list of queries to perform. Useful to pass multiple if you aren\'t sure if it is "toggle" or "switch", or "infinite scroll" or "infinite load", for example.')->required()
33-
31+
->raw('queries', [
32+
'description' => 'List of queries to perform, pass multiple if you aren\'t sure if it is "toggle" or "switch", for example',
33+
'type' => 'array',
34+
'items' => [
35+
'type' => 'string',
36+
'description' => 'Search query',
37+
],
38+
])->required()
3439
->raw('packages', [
3540
'description' => 'Package names to limit searching to from application-info. Useful if you know the package(s) you need. i.e. laravel/framework, inertiajs/inertia-laravel, @inertiajs/react',
3641
'type' => 'array',
@@ -54,7 +59,7 @@ public function handle(array $arguments): ToolResult|Generator
5459
$packagesFilter = array_key_exists('packages', $arguments) ? $arguments['packages'] : null;
5560

5661
$queries = array_filter(
57-
array_map('trim', explode('###', $arguments['queries'])),
62+
array_map('trim', $arguments['queries']),
5863
fn ($query) => $query !== '' && $query !== '*'
5964
);
6065

@@ -90,6 +95,7 @@ public function handle(array $arguments): ToolResult|Generator
9095
'token_limit' => $tokenLimit,
9196
'format' => 'markdown',
9297
];
98+
9399
try {
94100
$response = $this->client()->asJson()->post($apiUrl, $payload);
95101

0 commit comments

Comments
 (0)