Appearance
Livewire 3 Cheat Sheet / Summary
Livewire is a Laravel package for creating dynamic web applications without extensive JavaScript. It offers real-time, reactive components, simplifying the development of interactive user interfaces.
This page tries to provide a convenient way of finding the information related to the Livewire functionality used in our course.
TIP
We have also made a similar page for Laravel with the same purpose, you can find that here: Laravel Cheat Sheet / Summary
This page explains just a part of what Livewire can do and provides shorthand information. For more Livewire features and in-depth information please take a look at the following resources:
If you're using the TALL stack (Tailwind, AlpineJS, Livewire & Laravel), check out these additional resources for the stack's other technologies :
Create & render a component
shell
# Create a Livewire component called ProductCatalog
$ php artisan make:livewire ProductCatalog
# OR
$ php artisan livewire:make ProductCatalog
# Creates a component class which contains the logic (in app/Livewire)
# Creates a blade view (in resources/views/livewire)
# Create a component in a subdirectory
$ php artisan make:livewire Shop\\ProductCatalog
# OR
$ php artisan make:livewire shop.product-catalog
# Create a Livewire component called ProductCatalog
$ php artisan make:livewire ProductCatalog
# OR
$ php artisan livewire:make ProductCatalog
# Creates a component class which contains the logic (in app/Livewire)
# Creates a blade view (in resources/views/livewire)
# Create a component in a subdirectory
$ php artisan make:livewire Shop\\ProductCatalog
# OR
$ php artisan make:livewire shop.product-catalog
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Once a Livewire component is made you can render it in other blade views:
blade
{{-- Render a component --}}
<livewire:product-catalog />
{{-- OR --}}
@livewire('product-catalog')
{{-- Render a component --}}
<livewire:product-catalog />
{{-- OR --}}
@livewire('product-catalog')
1
2
3
4
2
3
4
Or render it in a route when used as a full-page component:
php
Route::get('catalog', ProductCatalog::class)->name('catalog');
Route::get('catalog', ProductCatalog::class)->name('catalog');
1
Component Class
Each Livewire component has an associated class which is found in the app/Livewire
directory. This class handles logic for that specific component and can act as a controller (in our course this is always the case).
By default it has a render method that returns the associated blade view.
Below is an example of a Livewire (full-page) component class which demonstrates the commonly used features.
php
// Import anything you use
use Livewire\Component;
use Livewire\WithPagination;
use App\Models\Product;
use Livewire\Attributes\Layout;
class ProductCatalog extends Component
{
// For full-page components Livewire, by default, will look for a layout at resources/views/components/layouts/app.blade.php
// Set a different layout file using Livewire\Attributes\Layout, second argument is to fill the slots directly
#[Layout('layouts.app-layout', [
'title' => 'Product catalog',
'description' => 'Take a look at our product catalog'
])]
// You can also set a global layout in the config at 'config/livewire.php', replacing the default
// Make use of Livewire pagination
use WithPagination;
// Create public properties
public string $search = '';
public $selectedProduct = null;
// Make a public function, accessible in the component view
public function selectProduct($productId)
{
$this->selectedProduct = Product::find($productId);
}
// You can add a mount function which only gets called once when component is loaded
public function mount()
{
$this->selectedProduct = Product::first();
}
// render function is called on first render & everytime a public property changes
public function render() {
// Fetch products and use the pagination feature
$products = Product::where('name', 'like', '%' . $this->search . '%')->paginate(10);
// Return the related blade view and pass data to it (associative array)
return view('livewire.product-catalog', [
'products' => $products,
]);
// You can use the compact function to simplify (when names are equal)
return view('livewire.product-catalog', compact('products');
}
}
// Import anything you use
use Livewire\Component;
use Livewire\WithPagination;
use App\Models\Product;
use Livewire\Attributes\Layout;
class ProductCatalog extends Component
{
// For full-page components Livewire, by default, will look for a layout at resources/views/components/layouts/app.blade.php
// Set a different layout file using Livewire\Attributes\Layout, second argument is to fill the slots directly
#[Layout('layouts.app-layout', [
'title' => 'Product catalog',
'description' => 'Take a look at our product catalog'
])]
// You can also set a global layout in the config at 'config/livewire.php', replacing the default
// Make use of Livewire pagination
use WithPagination;
// Create public properties
public string $search = '';
public $selectedProduct = null;
// Make a public function, accessible in the component view
public function selectProduct($productId)
{
$this->selectedProduct = Product::find($productId);
}
// You can add a mount function which only gets called once when component is loaded
public function mount()
{
$this->selectedProduct = Product::first();
}
// render function is called on first render & everytime a public property changes
public function render() {
// Fetch products and use the pagination feature
$products = Product::where('name', 'like', '%' . $this->search . '%')->paginate(10);
// Return the related blade view and pass data to it (associative array)
return view('livewire.product-catalog', [
'products' => $products,
]);
// You can use the compact function to simplify (when names are equal)
return view('livewire.product-catalog', compact('products');
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Directives
In the blade view of a Livewire component (found in the resources/views/livewire
directory) you can make use of some handy Livewire HTML directives. Below are the directives discussed in the course, for more take a look at the Livewire documentation.
wire:click
The wire:click directive allows you to define actions to execute when an element, such as a button, is clicked.
blade
{{-- Call an action on click (with parameters if needed --}}
<a href="#" wire:click="selectProduct({{ $product->id }})">{{ $product->name }}</a>
{{-- Add .prevent to prevent default browser behaviour when needed --}}
<a href="#" wire:click.prevent="selectProduct({{ $product->id }})">{{ $product->name }}</a>
{{-- Call an action on click (with parameters if needed --}}
<a href="#" wire:click="selectProduct({{ $product->id }})">{{ $product->name }}</a>
{{-- Add .prevent to prevent default browser behaviour when needed --}}
<a href="#" wire:click.prevent="selectProduct({{ $product->id }})">{{ $product->name }}</a>
1
2
3
4
5
2
3
4
5
wire:submit
The wire:submit
directive links a form to a Livewire action for submission.
blade
<form wire:submit="submitContactForm">
{{-- Form fields here, use wire:model (see below) for the inputs --}}
<button type="submit">Submit</button>
</form>
<form wire:submit="submitContactForm">
{{-- Form fields here, use wire:model (see below) for the inputs --}}
<button type="submit">Submit</button>
</form>
1
2
3
4
2
3
4
REMARK
- For
wire:submit
Livewire automatically callsevent.preventDefault()
as this is (almost) always the desired behaviour. Use the.prevent
modifier on other event listeners where needed. - Livewire also disables form submit buttons and marks all form inputs as
readonly
while submitting so users cannot submit the same form again.
wire:model
The wire:model
directive creates two-way data binding for input fields, ensuring that changes in an input field are synchronized with the associated property in the Livewire component.
blade
<input type="text" wire:model="search" placeholder="Search products">
<input type="text" wire:model="search" placeholder="Search products">
1
By default, Livewire sends network requests when an action, such as wire:click or wire:submit, is performed. However, it does not send a network request when a wire:model input is updated.
For this Livewire provides modifiers to give you control over when a network request is send. Below is a list of all currently available modifiers.
Modifier | Description |
---|---|
.live | Sends updates as a user types with a 150ms delay. |
.live.debounce.[?]ms | Debounce the sending of updates by the specified millisecond delay. |
.live.throttle.[?]ms | Throttle network request updates by the specified millisecond interval. |
.blur | Only send updates on the blur event. |
.change | Only send updates on the change event. |
.lazy | An alias for .change . |
.number | Cast the text value of an input to an integer on the server. |
.fill | Use the initial value provided by a "value" HTML attribute on page-load. |
Example: Adding the .live.debounce.500ms
modifier makes sure a network request is send each time a user has changed the input with a delay of 500 milliseconds.
blade
<input type="text" wire:model.live.debounce.500ms="search" placeholder="Search products">
<input type="text" wire:model.live.debounce.500ms="search" placeholder="Search products">
1
wire:loading
The wire:loading
directive is used to conditionally display loading indicators.
blade
{{-- Only shown during Livewire actions --}}
<div wire:loading> Loading... </div>
{{-- Show content when not doing any Livewire actions --}}
<div wire:loading.remove> Not doing any actions right now </div>
{{-- Use a class during Livewire actions --}}
<button wire:loading.class="opacity-50"> Save </button>
{{-- Remove a class during Livewire actions --}}
<button class="bg-blue-500" wire:loading.class.remove="bg-blue-500"> Save </button>
{{-- Add an attribute during Livewire actions --}}
<button type="button" wire:click="remove" wire:loading.attr="disabled"> Remove </button>
{{-- Target a specific action OR property change --}}
<div wire:loading wire:target="remove"> Removing product... </div>
<div wire:loading wire:target="search"> Search input has been changed </div>
{{-- Target a specific action with a specific parameter --}}
<div wire:loading wire:target="remove({{ $product->id }})"> Removing product... </div>
{{-- Customize the display property on show (default = none on hide & inline-block on show) --}}
<div wire:loading.inline-flex>...</div>
<div wire:loading.inline>...</div>
<div wire:loading.block>...</div>
<div wire:loading.table>...</div>
<div wire:loading.flex>...</div>
<div wire:loading.grid>...</div>
{{-- Delay a loading indicator --}}
<div wire:loading.delay.shortest>...</div> <!-- 50ms -->
<div wire:loading.delay.shorter>...</div> <!-- 100ms -->
<div wire:loading.delay.short>...</div> <!-- 150ms -->
<div wire:loading.delay>...</div> <!-- 200ms -->
<div wire:loading.delay.long>...</div> <!-- 300ms -->
<div wire:loading.delay.longer>...</div> <!-- 500ms -->
<div wire:loading.delay.longest>...</div> <!-- 1000ms -->
{{-- Only shown during Livewire actions --}}
<div wire:loading> Loading... </div>
{{-- Show content when not doing any Livewire actions --}}
<div wire:loading.remove> Not doing any actions right now </div>
{{-- Use a class during Livewire actions --}}
<button wire:loading.class="opacity-50"> Save </button>
{{-- Remove a class during Livewire actions --}}
<button class="bg-blue-500" wire:loading.class.remove="bg-blue-500"> Save </button>
{{-- Add an attribute during Livewire actions --}}
<button type="button" wire:click="remove" wire:loading.attr="disabled"> Remove </button>
{{-- Target a specific action OR property change --}}
<div wire:loading wire:target="remove"> Removing product... </div>
<div wire:loading wire:target="search"> Search input has been changed </div>
{{-- Target a specific action with a specific parameter --}}
<div wire:loading wire:target="remove({{ $product->id }})"> Removing product... </div>
{{-- Customize the display property on show (default = none on hide & inline-block on show) --}}
<div wire:loading.inline-flex>...</div>
<div wire:loading.inline>...</div>
<div wire:loading.block>...</div>
<div wire:loading.table>...</div>
<div wire:loading.flex>...</div>
<div wire:loading.grid>...</div>
{{-- Delay a loading indicator --}}
<div wire:loading.delay.shortest>...</div> <!-- 50ms -->
<div wire:loading.delay.shorter>...</div> <!-- 100ms -->
<div wire:loading.delay.short>...</div> <!-- 150ms -->
<div wire:loading.delay>...</div> <!-- 200ms -->
<div wire:loading.delay.long>...</div> <!-- 300ms -->
<div wire:loading.delay.longer>...</div> <!-- 500ms -->
<div wire:loading.delay.longest>...</div> <!-- 1000ms -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
wire:key
wire:key
is an attribute used within Blade templates in Livewire to help track and efficiently update elements within loops. It's essential to ensure that each wire:key
value is unique within the scope of the loop it's used in to prevent rendering issues.
blade
@foreach($products as $product)
<div wire:key="{{ $product->id }}">
{{ $product->name }}
</div>
@endforeach
@foreach($products as $product)
<div wire:key="{{ $product->id }}">
{{ $product->name }}
</div>
@endforeach
1
2
3
4
5
2
3
4
5
Example component view
This example view (for a product catalog component) uses the class discussed previously in Livewire - Component Class. It uses most of the Livewire functionalities we use in the course.
blade
<div>
{{-- Use wire:model for two-way data binding --}}
<input type="text" wire:model.live="search" placeholder="Search products">
{{-- Use wire:loading to show content during Livewire action --}}
<div wire:loading>
Loading updated ...
</div>
<div>
<h2>Selected Product:</h2>
<p>{{ $selectedProduct->name }}</p>
</div>
<ul>
@foreach($products as $product)
{{-- Use wire:key in loops so Livewire can keep track --}}
<li wire:key="product-{{ $product->id }}">
{{-- Use wire:click to call a function in the class on click --}}
<a href="#" wire:click.prevent="selectProduct({{ $product->id }})">
{{ $product->name }}
</a>
</li>
@endforeach
</ul>
{{-- Render pagination links --}}
{{ $products->links() }}
{{-- To customize the pagination links -> php artisan livewire:publish --pagination --}}
{{-- You can then find the files in resources/views/vendor/livewire --}}
</div>
<div>
{{-- Use wire:model for two-way data binding --}}
<input type="text" wire:model.live="search" placeholder="Search products">
{{-- Use wire:loading to show content during Livewire action --}}
<div wire:loading>
Loading updated ...
</div>
<div>
<h2>Selected Product:</h2>
<p>{{ $selectedProduct->name }}</p>
</div>
<ul>
@foreach($products as $product)
{{-- Use wire:key in loops so Livewire can keep track --}}
<li wire:key="product-{{ $product->id }}">
{{-- Use wire:click to call a function in the class on click --}}
<a href="#" wire:click.prevent="selectProduct({{ $product->id }})">
{{ $product->name }}
</a>
</li>
@endforeach
</ul>
{{-- Render pagination links --}}
{{ $products->links() }}
{{-- To customize the pagination links -> php artisan livewire:publish --pagination --}}
{{-- You can then find the files in resources/views/vendor/livewire --}}
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$wire object
The $wire object is an automatically injected 'magic' JavaScript object that is available within the scope of your Livewire component's view. It acts as a bridge between your Livewire component's backend (PHP) and frontend (JavaScript/HTML).
Alpine is our frontend Javascript framework and thus also has access to this object which is the main use case in our course. In fact, under the hood, every Livewire component is also an Alpine component.
Documentation propertiesDocumentation actions
blade
{-- Access a property from our class --}
$wire.propertyName
{-- Access a function from our class --}
$wire.functionName()
{-- Set a certain value for a property --}
$wire.set('propertyName', 'newValue')
{-- Example - Keeping client state with Alpine and passing it to the PHP function in our Livewire class --}
<div x-data="{ todo: '' }">
<input type="text" x-model="todo">
<button x-on:click="$wire.addTodo(todo)">Add Todo</button>
</div>
{-- Access a property from our class --}
$wire.propertyName
{-- Access a function from our class --}
$wire.functionName()
{-- Set a certain value for a property --}
$wire.set('propertyName', 'newValue')
{-- Example - Keeping client state with Alpine and passing it to the PHP function in our Livewire class --}
<div x-data="{ todo: '' }">
<input type="text" x-model="todo">
<button x-on:click="$wire.addTodo(todo)">Add Todo</button>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Validation
Livewire's #[Validate]
attribute simplifies the process of implementing validation rules directly in component methods. Livewire will automatically run the properties validation rules before each update.
You can find all the available validation rules in the Laravel validation documentation
php
use Livewire\Attributes\Validate;
// ...
#[Validate('required|min:3')]
public $newName = '';
use Livewire\Attributes\Validate;
// ...
#[Validate('required|min:3')]
public $newName = '';
1
2
3
4
5
6
2
3
4
5
6
You can display an error when validation fails by using the @error
directives:
blade
@error('newName') <span class="error">{{ $message }}</span> @enderror
@error('newName') <span class="error">{{ $message }}</span> @enderror
1
You can pass a custom attribute name into the validation message that is shown by using the as
parameter:
php
#[Validate('required|min:3', as: 'name of the person')]
public $newName = '';
#[Validate('required|min:3', as: 'name of the person')]
public $newName = '';
1
2
2
Below is an example using these validation features on a property that holds multiple values in an associative array:
php
#[Validate([
'formData.username' => 'required|unique:users,username',
'formData.age' => 'required|numeric'
], as: [
'formData.username' => 'Username',
'formData.age' => 'Age'
])]
public $formData = ['username' => null, 'age' => null];
#[Validate([
'formData.username' => 'required|unique:users,username',
'formData.age' => 'required|numeric'
], as: [
'formData.username' => 'Username',
'formData.age' => 'Age'
])]
public $formData = ['username' => null, 'age' => null];
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
For unique
validation rules for create
and update
you have to use the rules()
method instead of the #[Validate]
attribute.
php
// #[Validate('required|size:36|unique:records,mb_id,id', as: 'MusicBrainz ID')] // DOES NOT WORK
public $mb_id = null;
// special validation rule for mb_id (unique:records,mb_id,id) for insert and update!
public function rules()
{
return [
'mb_id' => "required|size:36|unique:records,mb_id,{$this->id}",
];
}
// $validationAttributes is used to replace the attribute name in the error message
protected $validationAttributes = [
'mb_id' => 'MusicBrainz ID',
];
// #[Validate('required|size:36|unique:records,mb_id,id', as: 'MusicBrainz ID')] // DOES NOT WORK
public $mb_id = null;
// special validation rule for mb_id (unique:records,mb_id,id) for insert and update!
public function rules()
{
return [
'mb_id' => "required|size:36|unique:records,mb_id,{$this->id}",
];
}
// $validationAttributes is used to replace the attribute name in the error message
protected $validationAttributes = [
'mb_id' => 'MusicBrainz ID',
];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Reset the error bag when needed (f.e. on update).
php
$this->resetErrorBag();
$this->resetErrorBag();
1
Form Objects
Livewire Form objects provide a more structured way to handle complex forms compared to using public properties in component classes. They are essentially classes containing:
- Public properties for form inputs.
- Validation rules for each property.
- Methods for CRUD actions and browser notifications.
Unlike a full Livewire component, a Form object does not have a separate view file or lifecycle methods like render()
or mount()
.
Commands
shell
$ php artisan livewire:form ProductForm
$ php artisan livewire:form ProductForm
1
Example Form Object
php
<?php
namespace App\Livewire\Forms;
use App\Models\Product;
use Livewire\Attributes\Validate;
use Livewire\Form;
class ProductForm extends Form
{
public $id = null;
#[Validate('required', as: 'name of the product')]
public $name = null;
#[Validate('required|numeric|min:0', as: 'price')]
public $price = null;
#[Validate('required|exists:genres,id', as: 'genre')]
public $category_id = null;
// read the selected product
public function read($product)
{
$this->id = $product->id;
$this->name = $prodcut->name;
$this->price = $product->price;
$this->category_id = $product->category_id;
}
// create a new product
public function create()
{
$this->validate();
Product::create([
'name' => $this->name,
'price' => $this->price,
'category_id' => $this->category_id,
]);
}
// update the selected product
public function update(Product $product) {
$this->validate();
$product->update([
'name' => $this->name,
'price' => $this->price,
'category_id' => $this->category_id,
]);
}
// delete the selected product
public function delete(Product $product)
{
$product->delete();
}
}
<?php
namespace App\Livewire\Forms;
use App\Models\Product;
use Livewire\Attributes\Validate;
use Livewire\Form;
class ProductForm extends Form
{
public $id = null;
#[Validate('required', as: 'name of the product')]
public $name = null;
#[Validate('required|numeric|min:0', as: 'price')]
public $price = null;
#[Validate('required|exists:genres,id', as: 'genre')]
public $category_id = null;
// read the selected product
public function read($product)
{
$this->id = $product->id;
$this->name = $prodcut->name;
$this->price = $product->price;
$this->category_id = $product->category_id;
}
// create a new product
public function create()
{
$this->validate();
Product::create([
'name' => $this->name,
'price' => $this->price,
'category_id' => $this->category_id,
]);
}
// update the selected product
public function update(Product $product) {
$this->validate();
$product->update([
'name' => $this->name,
'price' => $this->price,
'category_id' => $this->category_id,
]);
}
// delete the selected product
public function delete(Product $product)
{
$product->delete();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
HOW TO USE THE FORM OBJECT
- Use the Form object in your component class.
- With
public ProductForm $form;
, the public property$form
is of typeProductForm
and contains all the properties and methods of theProductForm
class - Now we can use the
$form
object in our component class and in the view with the$form
prefix - Us the
$form
properties in the view:$form->name
to echo the title of the productwire:model="form.name"
to bind the input element to the$form->name
property@error('form.name')
to show the error message for the$form->name
property
- Use the
$form
methodes in the component:$this->form->create()
to create a new product$this->form->update($product)
to update the selected product$this->form->delete($product)
to delete the selected product :::