Upgrading From 1.x

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

  1. Update the livewire/livewire dependency in your composer.json file to ^2.0
  2. Run composer update livewire/livewire
  3. Run php artisan view:clear
  4. Run php artisan livewire:publish --assets (If you published the assets before)

Update Your Application Code

Here are the breaking changes and their upgrade instructions in order of impact:

  1. Updated: $updatesQueryString to $queryString
  2. Removed: Route::livewire()
  3. Removed: Turbolinks Support
  4. Changed: assertSet()
  5. Removed: Property Casters
  6. Updated: Pagination Views
  7. Updated: JavaScript Hooks
  8. 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:

class Search extends Component
{
    // Before
    protected $updatesQueryString = ['search']

    // After
    protected $queryString = ['search']
}

Aside from a new property name, there are 2 significant changes to the inner workings:

  1. Property values are now automatically set to initial values from the query string on page load
  2. The query string system now uses the browser's history.pushState API instead of history.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:

class Search extends Component
{
    ...

    public function mount()
    {
        // No need for code like this anymore.
        // The search property will now be automatically set.
        $this->search = request()->query('search', '');
    }
}

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.

// Before
Route::livewire('/post', 'show-posts');

// After
Route::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:

protected function mapWebRoutes()
{
    Route::middleware('web')
        ->namespace($this->namespace) // Remove me
        ->group(base_path('routes/web.php'));
}

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:

<!-- Before -->
<html>
    <body>
        @yield('content')

        @livewireScripts
    </body>
</html>

<!-- After -->
<html>
    <body>
        {{ $slot }}

        @livewireScripts
    </body>
</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.

// Before
Route::livewire('/post', ShowPosts::class)
    ->layout('layouts.base')
    ->section('body');

// After
class ShowPosts extends Component
{
    public function render()
    {
        return view('livewire.show-posts')
            ->extends('layouts.base')
            ->section('body');
    }
}

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:

class ShowPosts extends Component
{
    public function render()
    {
        return view('livewire.show-posts')
            ->layout('layouts.base')
            ->slot('body');
    }
}

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:

...
    @livewireScripts
    <script src="https://cdn.jsdelivr.net/gh/livewire/[email protected]/dist/livewire-turbolinks.js" data-turbolinks-eval="false"></script>
</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:

  1. People mostly used these for properties that are instances of Collection and DateTime. These are now automatically cast out of the box
  2. Not many users use (or are even aware) of this feature to begin with
  3. There are other ways to accomplish this exact same functionality

Here are a few examples:

// Before
public $foo;

protected $casts = ['foo' => 'collection'];

public function mount()
{
    $this->foo = collect(['foo', 'bar']);
}

// After
// (Collections are automatically cast now)
public $foo;

public function mount()
{
    $this->foo = collect(['foo', 'bar']);
}
// Before
class AllCaps implements Castable {
    public function cast($value)
    {
        return strtoupper($value);
    }

    public function uncast($value)
    {
        return strtolower($value);
    }
}

class SomeComponent extends Component
{
    public $foo;

    protected $casts = ['foo' => AllCaps::class];

    ....
}

// After
class SomeComponent extends Component
{
    public $foo;

    public function hydrateFoo($value)
    {
        $this->foo = strtoupper($value);
    }

    public function dehydrateFoo($value)
    {
        $this->foo = strtolower($value);
    }

    ....
}

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:

class ShowPosts extends Component
{
    use WithPagination;

    protected $paginationTheme = 'bootstrap';

    ...
}

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:

  1. Copy the view source from GitHub
  2. Paste it into a new blade file anywhere you see fit. For example, we'll say: resources/views/pagination-links.blade.php
  3. Now reference it in your Blade view by passing it into the ->links() method:
{{ $posts->links('pagination-links') }}

Updated: JavaScript Hooks

V2 offers the same JavaScript hooks as V1, but with three distinct updates:

  1. Their names are different
  2. The parameter orders have been updated to be more consistent
  3. 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

...
    @livewireScripts

    // Before
    <script src="https://cdn.jsdelivr.net/gh/livewire/[email protected]/dist/livewire-vue.js"></script>

    // After
    <script src="https://cdn.jsdelivr.net/gh/livewire/[email protected]/dist/livewire-vue.js"></script>
</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
← Previous Topic

Reference

Next Topic →

Installation