Upgrading From 1.x
- V2 is Here! 🎉
- Update Your Composer Version
- Update Your Alpine Version
- Update Your Application Code
- Removed: Turbolinks Support
- Changed:
assertSet()
- Removed: Property Casters
- Updated: Pagination Views
- Updated: JavaScript Hooks
- Updated: VueJS Support
- Signing Off
V2 is Here! 🎉
Before we get into the technical upgrade stuff, you might be interested in what philosophical underpinnings are behind these changes.
- Livewire is declarative. Rather than providing an endless set of utilities for interacting with the front-end. Livewire aims to make front-end interactions a "side-effect" of your state (i.e. component properties). For example, with the new
$queryString
API, rather than providing methods to manually update the browser's query string from the backend, you declare which component properties you want to be reflected in the front-end's query string with the$queryString
property. - Livewire is anti-boilerplate. By allowing developers to set eloquent models as properties and
wire:model
(bind) to them directly, we're able to cut out SO much boilerplate code. To further kill the boilerplate, in V2, component parameters are now automatically assigned to public properties by matching their name. Now,mount()
methods are only used for things they MUST be used for, not for simply forwarding parameters to properties. Kill the noise. - Livewire is a back-end interface at its core. The
wire:click
stuff is just sugar that makes the interface easy to use. With the addition of$wire
, the underlying power is now apparent: Livewire allows you to interface with backend code, directly and declaratively without the need for imperative/boilerplatey patterns like axios.post(), RESTfull endpoints, controllers, etc... - Livewire is simple to use. Of all the philosophies I hold, I hold this one the strongest. Livewire should always remain ridiculously easy to use. My goal is that you can easily remember and almost guess its APIs. Before introducing any feature, I scour existing patterns and APIs in Laravel to see if Livewire can use that shared knowledge as leverage for new adopters. A small example is the new
$rules
property. I could have named it anything, but why would I name it anything besides$rules
(a precedent set by Request objects in Laravel)? If I don't think an API is easy, intuitive, and clear, I wait on the feature and let it simmer until something clear and beautiful emerges. (Or at least that's my goal.)
Update Your Composer Version
- Update the
livewire/livewire
dependency in yourcomposer.json
file to^2.0
- Run
composer update livewire/livewire
- Run
php artisan view:clear
- Run
php artisan livewire:publish --assets
(If you published the assets before)
Update Your Alpine Version
If you are using AlpineJS with Livewire V2, make sure you are on version 2.7.0
or greater.
For example:
1<script src="https://cdn.jsdelivr.net/gh/alpinejs/[email protected]/dist/alpine.min.js" defer></script>
Update Your Application Code
Here are the breaking changes and their upgrade instructions in order of impact:
- Updated:
$updatesQueryString
to$queryString
- Removed: Route::livewire()
- Removed: Turbolinks Support
- Changed:
assertSet()
- Removed: Property Casters
- Updated: Pagination Views
- Updated: JavaScript Hooks
- Updated: VueJs Support
Updated: $updatesQueryString
to $queryString
Livewire 1.x had a more primitive utility for manipulating the browser's query string based on property values. In V2, there is a much more advanced utility for manipulating the query string.
The first breaking change is $updatesQueryString
has been changed to $queryString
:
1class Search extends Component2{3 // Before4 protected $updatesQueryString = ['search']5 6 // After7 protected $queryString = ['search']8}
Aside from a new property name, there are 2 significant changes to the inner workings:
- Property values are now automatically set to initial values from the query string on page load
- The query string system now uses the browser's
history.pushState
API instead ofhistory.replaceState
(which means you can now click the back button in a browser to revisit old query string changes)
Because the query string system now automatically sets initial values, there is no need for doing that in the mount()
method anymore:
1class Search extends Component 2{ 3 ... 4 5 public function mount() 6 { 7 // No need for code like this anymore. 8 // The search property will now be automatically set. 9 $this->search = request()->query('search', '');10 }11}
Removed: Route::livewire()
Livewire 1.x allowed you to register a component with a route for the entire page using the Route::livewire()
method. Livewire 2.0 now allows you to pass Livewire components directly into routes using the standard Route::get()
method and the fully qualified namespace.
1// Before2Route::livewire('/post', 'show-posts');3 4// After5Route::get('/post', \App\Http\Livewire\ShowPosts::class);
The first thing to note is that if you are using Laravel 7, you will need to remove the namespace(...)
line from app/Providers/RouteServiceProvider.php
:
1protected function mapWebRoutes()2{3 Route::middleware('web')4 ->namespace($this->namespace) // Remove me5 ->group(base_path('routes/web.php'));6}
This is done by default in Laravel 8, but if you are on Laravel 7, you will need to remove this to be able to pass a Livewire class into Route::get()
. Otherwise, Laravel will prepend a namespace to ALL classes passed into Route::get()
.
By default in 1.x, Livewire renders your page-level components using a traditional Blade layout located in resources/layouts/app.blade.php
. In 2.0, Livewire uses the same layout file as a default, however, it now expects you are using the new Blade component $slot
syntax in the layout. For example:
1<!-- Before --> 2<html> 3 <body> 4 @yield('content') 5 6 @livewireScripts 7 </body> 8</html> 9 10<!-- After -->11<html>12 <body>13 {{ $slot }}14 15 @livewireScripts16 </body>17</html>
If you manually configured a layout for the route in your routes file, the ->layout()
method has now been moved to a new method called ->extends()
and placed in the render function.
1// Before 2Route::livewire('/post', ShowPosts::class) 3 ->layout('layouts.base') 4 ->section('body'); 5 6// After 7class ShowPosts extends Component 8{ 9 public function render()10 {11 return view('livewire.show-posts')12 ->extends('layouts.base')13 ->section('body');14 }15}
If you wish to update your manually configured layouts to the new $slot
syntax, you can specify them using the new ->layout()
method. This method will use the $slot
by default, but you can also configure the component to render into a named slot using the ->slot()
method:
1class ShowPosts extends Component2{3 public function render()4 {5 return view('livewire.show-posts')6 ->layout('layouts.base')7 ->slot('body');8 }9}
Removed: Turbolinks Support
Livewire no longer supports Turbolinks out of the box.
If you want to continue using Turbolinks in your Livewire application, you will have to include the Turbolinks adapter alongside Livewire's JavaScript assets:
1...2 @livewireScripts3 <script src="https://cdn.jsdelivr.net/gh/livewire/[email protected]/dist/livewire-turbolinks.js" data-turbolinks-eval="false"></script>4</body>
As this adapter is new, you may run into issues related Turbolinks functionality, if that is the case, please submit an issue on the adapter's repository.
Changed: assertSet()
In Livewire V1, the testing method assertSet('property', 'value')
tested against data in the JavaScript-safe Livewire payload, rather than asserting against the value of a property on the actual Livewire component's PHP instance. This made it impossible to test computed properties from assertSet()
.
In V2, assertSet()
now behaves how you would expect: making assertions on data in the actual PHP instance, and if you want to assert against payload data, you can now use the new assertPayloadSet()
.
For most people, this won't change a thing. However, if during your upgrade you are getting failures in your test suite around an assertSet()
, you should either refactor your test or use assertPayloadSet()
.
Removed: Property Casters
Property casters have been removed in Livewire V2. There are three reasons for this decision:
- People mostly used these for properties that are instances of
Collection
andDateTime
. These are now automatically cast out of the box - Not many users use (or are even aware) of this feature to begin with
- There are other ways to accomplish this exact same functionality
Here are a few examples:
1// Before 2public $foo; 3 4protected $casts = ['foo' => 'collection']; 5 6public function mount() 7{ 8 $this->foo = collect(['foo', 'bar']); 9}10 11// After12// (Collections are automatically cast now)13public $foo;14 15public function mount()16{17 $this->foo = collect(['foo', 'bar']);18}
1// Before 2class AllCaps implements Castable { 3 public function cast($value) 4 { 5 return strtoupper($value); 6 } 7 8 public function uncast($value) 9 {10 return strtolower($value);11 }12}13 14class SomeComponent extends Component15{16 public $foo;17 18 protected $casts = ['foo' => AllCaps::class];19 20 ....21}22 23// After24class SomeComponent extends Component25{26 public $foo;27 28 public function hydrateFoo($value)29 {30 $this->foo = strtoupper($value);31 }32 33 public function dehydrateFoo($value)34 {35 $this->foo = strtolower($value);36 }37 38 ....39}
Updated: Pagination Views
If you've paginated results by adding WithPagination
to a component and relied upon the default Livewire pagination links view using $posts->links()
, the views will have been updated from Bootstrap-4 to Tailwind.
Livewire V2 still supports Bootstrap-4 pagination, however, you have to configure it using the $paginationTheme
property on your component:
1class ShowPosts extends Component2{3 use WithPagination;4 5 protected $paginationTheme = 'bootstrap';6 7 ...8}
Even though V2 still supports Bootstrap-4, the pagination view has been updated to match Laravel 8. Therefore, it will differ slightly from the view previously used in V1. To use the exact view from V1:
- Copy the view source from GitHub
- Paste it into a new blade file anywhere you see fit. For example, we'll say:
resources/views/pagination-links.blade.php
- Now reference it in your Blade view by passing it into the
->links()
method:
1{{ $posts->links('pagination-links') }}
Updated: JavaScript Hooks
V2 offers the same JavaScript hooks as V1, but with three distinct updates:
- Their names are different
- The parameter orders have been updated to be more consistent
- In places where an instance of the "DomElement" wrapper was passed, now a native DOM element is passed
Here are the hook usages side by side for comparison:
V1 Names | V2 Names / Usages |
---|---|
livewire.hook('componentInitialized', (component) => {}) |
Livewire.hook('component.initialized', (component) => {}) |
livewire.hook('elementInitialized', (el, component) => {}) |
Livewire.hook('element.initialized', (el, component) => {}) |
livewire.hook('beforeElementUpdate', (from, to, component) => {}) |
Livewire.hook('element.updating', (fromEl, toEl, component) => {}) |
livewire.hook('afterElementUpdate', (node, component) => {}) |
Livewire.hook('element.updated', (el, component) => {}) |
livewire.hook('elementRemoved', (el, component) => {}) |
Livewire.hook('element.removed', (el, component) => {}) |
livewire.hook('messageSent', (component, message) => {}) |
Livewire.hook('message.sent', (message, component) => {}) |
livewire.hook('messageFailed', (component) => {}) |
Livewire.hook('message.failed', (message, component) => {}) |
livewire.hook('responseReceived', (component, response) => {}) |
Livewire.hook('message.received', (message, component) => {}) |
livewire.hook('afterDomUpdate', (component) => {}) |
Livewire.hook('message.processed', (message, component) => {}) |
livewire.hook('beforeDomUpdate', (component) => {}) |
Livewire.hook('message.received', (message, component) => {}) |
Note: in some instances, a message
object is now passed in instead of a response
object. response
can be accessed as a property of message
: message.response
Updated: VueJS Support
If your Livewire currently depends on the vue-plugin, you will need to upgrade from version 0.2.x
to 0.3.x
1...2 @livewireScripts3 4 // Before5 <script src="https://cdn.jsdelivr.net/gh/livewire/[email protected]/dist/livewire-vue.js"></script>6 7 // After8 <script src="https://cdn.jsdelivr.net/gh/livewire/[email protected]/dist/livewire-vue.js"></script>9</body>
Signing Off
Hopefully, the impact of this upgrade isn't much for you.
If you have questions or corrections to make to this document, please submit a GitHub issue on the repository.
As always, thanks for your support and thanks for using Livewire!
- Caleb