Livewire v3 Has Been Released

Published on

Caleb Porzio just released Livewire v3! In his talk at Laracon, he demoed a ton of the new features, some of which we'll cover here, and you can watch the full video:

Rewritten core

The core of Livewire has been totally rewritten from scratch. Rewriting the core was a huge undertaking, but it was necessary to accomplish everything that's included in this release. The new core architecture will also be much easier to maintain for Caleb and the Livewire core contributors.

Alpine utilization

The v3 core now utilizes Alpine to its full potential. Instead of including code for applying DOM updates, adding event listeners, etc., in both Livewire and Alpine, Livewire v3 utilizes Alpine for the heavy lifting. By registering Alpine plugins, Livewire now allows Alpine to do the heavy lifting while still providing the syntactic sugar you've come to love.

This also means Alpine is now included by default with Livewire, so there's no need to load Alpine via a CDN or NPM. It's automatically injected.

In addition, v3 utilizes the Alpine Morph plugin for diffing the DOM and applying updates instead of morphdom. This will lead to fewer DOM-diffing issues and overall better compatibility between Livewire and Alpine.

Improved component nesting

In the past, I considered nesting Livewire components an anti-pattern or something to avoid when possible because of potential performance implications. In v3, that won't be the case!

With reactive props, bundled requests, the new $parent property, and other improvements, v3 is improving component nesting quite a bit. We'll cover some of these improvements in more detail later in this post.

Improved property serialization

v2 tried to intelligently determine what type properties were on the initial render to rehydrate them as the same types on subsequent requests. v2 didn't support deeply nested hydration though. If you had a collection with mixed types, v2 wouldn't cast each collection item back to its original type. v3 is much smarter about this and stores the type of each item so it can cast them back to proper types on subsequent requests.

Bundled requests

In v2, when multiple components on a page were polling or listening for the same event, Livewire would send separate requests for each component every time it needed to talk to the server. v3 is much more efficient and bundles all those updates into one request.

Better defaults

Blade marker injection

DOM diffing is one of the most common issues you could run into with v2. The issue was typically caused by @if and similar Blade directives inserting or removing DOM elements. v3 attempts to circumvent those issues by injecting HTML comments (markers) where those Blade directives start and end. By looking for those markers, Livewire can match up new or removed DOM elements with a marker to place them correctly in the DOM.

1<form wire:submit="save">
2 {{-- ... --}}
3 
4 <!-- __BLOCK__ -->
5 @if ($success)
6 <div>Saved!</div>
7 @endif
8 <!-- ENDBLOCK -->
9 
10 <div>
11 <button>Save</button>
12 </div>
13<div>

wire:model is deferred by default

Originally, Livewire being "live" was a really cool feature, so it was made the default. Upon further thought and observing real-world usage, Caleb realized deferring wire:model requests was the better default. Most apps don't actually need input values to be synced with the server on every keystroke, so v3 flips the default behavior. The previous wire:model.defer functionality is the new default when using wire:model. wire:model.live has been added to replace the old default behavior on inputs that actually need to be "live".

1{{-- Now deferred by default --}}
2<input wire:model="name">
3 
4{{-- Syncs with the server on every keystroke --}}
5<input wire:model.live="name">

New features

Automatically injected assets

In v3, Livewire automatically injects its styles, scripts, and Alpine. There's no need to add <livewire:styles /> and <livewire:scripts /> or load Alpine in your projects anymore!

New default namespace

By default, v3 uses the App\Livewire namespace (and app/Livewire directory) instead of App\Http\Livewire now. There's a configuration option to keep the old namespace if you prefer that.

Reactive properties

Coming from frontend frameworks like React and Vue, some Livewire users automatically assumed properties they passed into nested components would be reactive to changes in the parent. Due to some limitations in v2, that wasn't actually possible. We had to rely on events or other workarounds to sync nested components. v3 adds support for "reactive" props. It's as simple as adding a #[Reactive] PHP attribute to the property in your component class.

1<?php
2 
3// ...
4use Livewire\Attributes\Reactive;
5 
6class InvoiceItem extends Component
7{
8 #[Reactive]
9 public $item;
10}

Form objects

Form objects are a new concept that can help keep component classes clean by abstracting away some of the form-specific code. You may want to put a component's form properties, validation, saving functionality, and more on form objects.

Here's a small example, but I'd recommend reading the documentation for a full list of the features!

1<?php
2 
3namespace App\Livewire\Forms;
4 
5use Livewire\Form;
6 
7class UserForm extends Form
8{
9 #[Rule('required')]
10 public $name = '';
11 
12 #[Rule(['required', 'email'])]
13 public $email = '';
14}
1<?php
2 
3namespace App\Livewire;
4 
5use Livewire\Component;
6use App\Models\Post;
7use App\Livewire\Forms\UserForm;
8 
9class UpdateProfile extends Component
10{
11 public UserForm $form;
12 
13 public function save()
14 {
15 auth()->user()->update(
16 $this->form->all()
17 );
18 
19 return $this->redirect('/profile');
20 }
21 
22 public function render()
23 {
24 return view('livewire.update-profile');
25 }
26}
1<form wire:submit="save">
2 <input type="text" wire:model="form.name">
3 <div>
4 @error('form.name')
5 <span class="error">{{ $message }}</span>
6 @enderror
7 </div>
8 
9 <input type="email" wire:model="form.email">
10 <div>
11 @error('form.email')
12 <span class="error">{{ $message }}</span>
13 @enderror
14 </div>
15 
16 <button type="submit">Save</button>
17</form>

wire:navigate (SPA mode)

Another new addition is wire:navigate. When using full-page Livewire components, you may now add the wire:navigate attribute to your links to enable a SPA-like experience. Instead of doing a full-page load, Livewire will add a loading indicator to the top of the page, fetch the contents of the new page in the background, then intelligently swap out the HTML on the page. The feature also supports prefetching and redirects and enables the ability for persisted elements.

1<a href="/profile" wire:navigate>Profile</a>

@persist

With the addition of wire:navigate, you may now have "persisted" elements that aren't reloaded when navigating through different pages. This feature will be handy for use cases like audio players that you want to continue playing audio while your users click through different pages.

To use the feature, wrap the element you want to be persisted in the @persist directive:

1@persist('player')
2 <audio src="{{ $episode->file }}" controls></audio>
3@endpersist

Lazy-loaded components

Livewire now supports lazy-loaded components. Sometimes, you have a component that might slow down initial page loads or is initially hidden inside a modal that you want to lazily load to keep the initial page load fast.

To lazy-load a component add a lazy attribute to the component in your Blade.

1<livewire:your-component lazy />

Livewire will skip rendering that component on the initial page load. Once all the other contents are loaded, a network request will be sent to fully render the component and the component will be inserted into the DOM.

$parent property

To communicate with "parent" components in previous versions of Livewire, you had to use events and listeners. In v3, there's a new $parent property you may use to call methods on parent components directly from children.

1<div>
2 <span>{{ $item->name }}</span>
3 
4 <button wire:click="$parent.remove({{ $item->id }})">Remove</button>
5</div>

Hybrid methods/evaluating JS from the backend

v3 allows you to write JavaScript methods in your backend components now. By adding the #[Js] attribute to a method on a component, you may write JavaScript code as a string and Livewire will expose that method to your frontend. When called via wire:click, the JavaScript will be executed on the frontend with no network requests being sent back to the server.

1<?php
2 
3// ...
4use Livewire\Attributes\Js;
5 
6class UsersTable extends Component
7{
8 public $filters = [];
9 
10 #[Js]
11 public function reset()
12 {
13 return <<<'JS'
14 $wire.filters = [];
15 JS;
16 }
17 
18 // ...
19}
1<div>
2 <div>
3 <input wire:model.live="filters.active" type="checkbox">
4 <label>Only active</label>
5 </div>
6 
7 <button wire:click="reset">Reset Filters</button>
8 
9 @foreach ($users as $user)
10 {{-- ... --}}
11 @endforeach
12</div>

Sometimes, it's useful to execute small bits of JavaScript from your backend methods. v3 enables that via the $this->js() method. Just call the method and pass in a string of JavaScript and Livewire will execute it on the frontend on the next render.

1public function save()
2{
3 // ...
4 
5 $this->js("alert('You just executed JS from the backend!')");
6}

PHP Attribute usage

PHP Attributes are a relatively new and powerful feature to PHP, and Livewire v3 utilizes them heavily for new and existing features.

#[Url]

The new #[Url] attribute replaces the $query property from v2. Add #[Url] above any property on your component and it'll be tracked in the query string.

1<?php
2 
3// ...
4use Livewire\Attributes\Url;
5 
6class UsersTable extends Component
7{
8 #[Url]
9 public $filters = [];
10 
11 // ...
12}

#[On]

The new #[On] attribute replaces the $listeners property from v2. Add #[On('some-event')] above a method on your component, and it will be executed whenever that event is dispatched.

1<?php
2 
3// ...
4use Livewire\Attributes\Url;
5 
6class UsersTable extends Component
7{
8 // ...
9 
10 #[On('filters-reset')]
11 public function resetFilters()
12 {
13 // ...
14 }
15}

#[Layout] and #[Title]

The new #[Layout] and #[Title] attributes allow you to set the layout view and title for full-page components. They may be added on the render method of the component or on the class itself.

1#[Layout('layouts.app')]
2public function render()
3{
4 return view('livewire.create-post');
5}

#[Computed]

Replacing v2's getSomeProperty syntax for computed properties, we have the #[Computed] attribute. It functions the same as the old syntax but has some new, really powerful additions.

In v2, computed properties could only be cached during a single request. In v3, you can cache a property across multiple requests and even across components. This will make caching expensive database queries a breeze!

1use Livewire\Attributes\Computed;
2 
3// Cache across requests
4#[Computed(persist: true)]
5public function user()
6{
7 return User::findOrFail($this->userId);
8}
9 
10// Cache across all instances of this component
11#[Computed(cache: true)]
12public function user()
13{
14 return User::findOrFail($this->userId);
15}

#[Rule]

The new #[Rule] attribute replaces the $rules property and rules method from v2. Add #[Rule(['required', 'max:150'])] to a property on your component to tell Livewire how the property should be validated.

1<?php
2 
3// ...
4use Livewire\Attributes\Rule;
5 
6class InvoiceItem extends Component
7{
8 #[Rule(['required', 'max:120'])]
9 public $itemName;
10 
11 // ...
12}

#[Locked]

The new #[Locked] attribute allows you to prevent a user from updating a property from the frontend. You may use this as a security measure on properties that users shouldn't be able to change.

1<?php
2 
3// ...
4use Livewire\Attributes\Locked;
5 
6class InvoiceItem extends Component
7{
8 #[Locked]
9 public $id;
10 
11 // ...
12}

Upgrading

The upgrade process for most apps will be pretty fast. There's a livewire:upgrade artisan command that's included in the beta to help with some of the more tedious changes, then you can follow the upgrade guide to ensure everything is up to date.

If you would rather leave the upgrade to Livewire experts, Caleb and I are offering a service to get you a jumpstart on the upgrade process. You can check that out here.

New website design and domain

In addition to all the new features and improvements, the Livewire docs site has been completely redesigned, rebuilt, and rewritten!

The new docs are much more thorough than the v2 docs. They cover each feature in detail and go into depth on how Livewire handles hydration, morphing, and nested components under the hood. I recommend reading through the entire site as you have time. It's a masterclass in building Livewire v3 apps!

Another big announcement is that the docs site is being moved to livewire.laravel.com. Go on over there and check it out!


This was a long post, but there were so many new features to cover! I hope you'll try v3 out soon, and feel free to reach out with feedback on the improvements.