Livewire v3 has just been announced at Laracon by Caleb Porzio. There are a ton of amazing new features so let's take a look.
Transcript: Hey everyone, Livewire v3 is now available. It was just announced at Laracon US by Caleb. There are a ton of amazing new features. So let's take a look and dive in. Let's take a look at the new navigate feature. So if you've ever worked with Inertia or React, then you're probably familiar with a single-page application. And when you navigate between pages, it feels like it's instant. Now with Livewire, components are reactive, but when you're switching between pages, you still have the browser refresh. And now in v3, there's a new navigate directive, which introduces the same kind of feel that you have with a single-page application. So what I've got here is a little demo application where we have a bunch of different pages, and we're going to check out the new navigate feature and see what the difference is and how it works. So what you can see here is when it starts to navigate between pages is that the browser completely reloads the page. So this is how things worked in v2. With v3, we can add the wire:navigate directive to our navigation, and we get a single-page application effect. Let's take a look. So let's switch to our editor and open up our navigation file. Here you can see that I have all the links defined to all our different pages. So in order to enable the navigate feature, all I have to do is add wire:navigate to each of these links. So if we switch back to our browser and we start navigating between pages, you can see that it feels instant. We even get a nice loading indicator whenever we navigate to a page that takes a bit longer to load. There's a loading bar at the top of the screen and there's a little loading indicator on the right side. This is something you can turn off, by the way. So how does this work? Well, Livewire keeps track of the mouse down event and will prevent the browser from actually visiting the page that you're clicking. Instead, it will do a request in the background and at the same time show a loading bar at the top of the page. And once the page is done, it replaces the actual content of the HTML whenever you release your mouse. Pretty neat, right? Besides the mouse down event, there's also an option to have a trigger on hover. This is cool for navigation menus like you see up here. So to enable it, we simply add .hover. That's it. Now Livewire will trigger a request in the background whenever someone hovers over one of these URLs. So the hover modifier may increase the server usage because it's firing off a bunch more requests. Livewire does add like a 60-millisecond rate before prefetching to mitigate some of the effects caused by the hover. So be careful when you're using it, especially when you have a lot of URLs. Now another cool feature related to wire:navigate is @persist directive. So you might imagine that you have a video on your website and you want to have an embedded like mini player on the bottom right corner of your browser. And when you navigate between pages, you want the video to keep on playing. So right now when you would do this whenever you would navigate between pages, it would actually reload the video. So what we need to prevent this is to add the persist directive. And it's super easy. Let's switch back to our editor and wrap our video in the persist directive. The persist directive expects a key, which is a unique identifier which Livewire keeps track of, which element should be preserved on the page. So let's add that. And let's move back to our browser, play the video. And as you can see, once I start navigating between pages, the video stays where it was and it keeps on playing. Now you might think, well, this is pretty cool, but how does it work when I use different pieces of JavaScript around my application? Well, there are a few things you have to take into account. First, script tags within the head tag are loaded once. So if you have two different JavaScript files on two separate pages, each of them will be loaded and evaluated once. So as you can see, I added a Hello World alert and that's getting fired on the initial page load. So when I start navigating between additional pages, I don't get the alert a second time. Let's add a second script tag and let's give it a Hello World two alert. When I switch to the browser and when I start navigating, you will see that I only get the Hello World two alert and not the initial one. So again, script tags are only initialized and evaluated once when they're inside the head tag. So one of the downsides, of course, is when you have a piece of JavaScript and a user still has an active session open and you deploy a new version of your application at the same time, he or she is still getting the old JavaScript. So as you can see in this example, I have a JavaScript file with built version one and it fired off an alert whenever I loaded the initial page. Now I'm going to change the built version to three and I'm going to add the data-navigate-track attribute to our script tag. And now when I go to a new page, you can see that the entire page has got reloaded and the alert was triggered once again. So this is a cool feature to make sure that your assets or the browser is reloaded whenever you change to a different version. So what about script tags that are inside the body tag? Well, these are evaluated every time a page loads. So let's clean this up a bit and let's add an alert. Now you can see every time I navigate between a page, it will fire off the alert. Now in some cases, you might think like, hey, I want this JavaScript tag on my page, but I only wanted to run once the entire session. So there's a data attribute for that as well. You can add data-navigate-once. So this will ensure that the script is evaluated once while navigating between pages. Okay, let's move on to lazy loading Livewire components. So imagine you have a dashboard and there are some statistics that you want to show, but they take a bit of time to calculate and for them to show up. So right now, the entire page would be loaded a bit slower because it's waiting for all the components to be ready. So with the new lazy loading feature, you can ensure that a component doesn't lower the response time when loading your page. The component will sit there and it will load in the background and it will appear once it's done loading. By default, it won't show anything while it's loading in the background, but if you want, you can add a placeholder. Simply open up the components and add a method called placeholder. You can return the HTML in line or return a blade view and this placeholder will appear while your component is loading. So let's take a look when we refresh the page. You can see that we now get a nice animation and after two seconds, the component will appear. Next up, we got reactive properties. So on this page here, I have an example of a users list and a recently created users list. These are two separate components. Well, actually, the recently created list is a child component. And by default, when we would add a new user, the recently created component does not know a new user got added. So in this example, if we add a new user, you can see that the list did not get updated. Now, if we take a quick look at the code, you can see that we're simply looking through our users and that there's a second reason users' components where we pass along the collection of users. In the recent users' components, you can see that we accept the users as an attribute. And in the blade view, you can see that we're simply sorting the user's collection by the created update and showing the users in that order. So in order to make the recently created users list update whenever we add a new user is by making a property on the component reactive. We can do so by simply using the reactive attribute. So here we got our collection of users and we simply add the reactive attribute. That's it. Let's move back to the browser, click Add New User, and as you can see now our child components got updated whenever we add a new user. Okay, let's move on to the next feature, which is called Motables. So in this example, I have a form where you can create a new post and we got a title, we got some content and a Create Post button. Now, let's assume that our content, our text area is some fancy live wire input components. So if you do, it was quite difficult to make a component behave as an actual model. Now with Livewire, you can make a component a model. So that means that you can set wire model and it will actually bind you through that value to our component. Let me show you, it makes more sense. So as you can see here, we're looping through posts, we got a form here, we got two input fields, we got our title and our body. And in this case, we're going to replace our text area with a new component. So our post components look like this, we got a title, we got our body, whenever we hit the button, we create a new post and we reset the properties. So let's create a new Livewire component called form/text area. So let's insert our Livewire text area component right here. And let's move the HTML into our component view. Okay, perfect. Now this text area has become generic, so wire model body doesn't make sense. So let's change this into wire model value instead. So now we add wire model body to our text area and give this a try and browser. You will see that it did not work. One, the body did not get included when the post is created and the form did not reset. So how to make a component model is simply by adding a public property that contains the value. In this case, the value of our text area. Next, we add a new Livewire attribute, which is called model. So we've given another try and enter our title and our content and attempt to create a new post. As you can see, the character validation is also working. So let's add additional few characters. And there we go. Our posts just got added to the database. So this is pretty cool, but there's even better feature. You can now extract this logic into form objects. This is especially useful when you're working with larger forms and you have to deal with a lot of properties. But it basically allows you to abstract the rules, but even also the creation of your models into a separate object. So you use the Livewire form command to create a new form. So let's do it right now. And we'll call it post form. Let's remove the validation rules and set a public property that is equal to the post form object. And let's move the validation rules into our post form object. Now we also need to make sure that we update our references. So instead of referencing this title, we need to make sure that everything is referencing this form. So let's make sure that we update our models as well inside our blade view. Okay, let's quickly try this in the browser. And there we go. All the validation rules still work. Now besides the validation rules, we can also move other logic in these objects as well, for example, the creation of our models. So let's create a public method called store and we'll replicate the same behavior. So we'll say posts, create and we'll pass all the data. And with all the data, I mean all public properties defined on this form object. So in this case, it will only return title and body. Now we can move back to our post component and remove the creation of the model like we did earlier. And instead, we'll reference our form object and call the store method. So now the validation and the storing of this form is abstracted into our form object. Okay, let's move on to another feature, which is called teleport. So you probably build an application where you have to deal with a model or like a dialog. And with Livewire 3, there's a new teleport directive, which makes building these kinds of forms quite easy because it allows you to teleport content from a component to outside of your component. That makes sense. Let's dive into code and let me explain how this works. So here we've got our settings page. So let's create a button that will open up our model and we're going to set an @click listener, which is Alpine. And we'll set the show model value to true. We'll later define this value in our app layout. Now within the same component, we might want to define what the contents of this model should be. So now let's use the teleport directive. The teleport directive accepts one parameter and this parameter is a query selector, which is basically saying, hey, I want this content to be teleported to this element or ID within the DOM. So let's reference a model body in this example. Make sure that we end our directive and inside this directive, we can add the contents for our model. So in this example, let's just create a simple div with a title saying "My Settings Model". Now our button was setting a show model property to true. We still have to define it. So let's add an x-data property to our body class and set show model to false by default. Now we want to create some sort of container for our model. So this is going to live in our layout. So this is only going to be defined once. And basically, anytime we use the teleport directive, the content is going to be teleported over to this little section that we defined in our app layout. So in our example, we got our settings components with our settings.blade.php. That's where we defined the contents of the settings model. And we're basically going to teleport contents to this div that we just defined just here. So let's give it some basic markup. So we got our model backdrop. Let's make sure that it's a fixed div that has some height, some with a nice background. And then we'll define our model body as well, which is going to be a simple div with a white background, some shadows, some rounded corners, all good stuff. And this is where the contents will be teleported to because we referenced the ID as model-body. And if you recall inside our settings blade, we set the teleport directive query selector to model-body. So when we now click the button, we should expect to have the show model property set to true, and the contents being teleported into the model-body div. So let's give it a try. And there you go. Our content was teleported over to the model that we've set on our layout. So this is pretty cool. I think it's a great addition to Livewire to make it easier to work with these types of components like modals or slide-overs. There's one thing to be aware of, though, is you must always teleport outside of the components. So what we did just here, teleporting contents to a div that lives on the body tag. That's fine. But you cannot teleport to a different Livewire component because on Subsequent Request, that code will be removed.
So these are some of the major new features in Livewire version 3, but this is definitely not all. So be sure to check out the documentation and read up on all new features and changes. This is still a beta release, so we might see some additional changes before the stable release ships. But nevertheless, let's take a look at the upgrade process. I have a ton of Livewire applications, and I thought it would be pretty great if some of these things could be automated. I've been contributing to an upgrade command that will speed up the migration process. It's still work in progress, and not everything has been automated, but it will definitely save you some time. Oh, and by the way, in case you're a regular expression fan, feel free to submit a PR with some additional upgrade automations. Anyways, let's dive in and take a look at what happens when we run the Livewire upgrade command. So you will see that this command will take a step-by-step approach and run us through the migration process from version 2 to version 3. Files will be modified in place, so make sure that you have a backup of your project before continuing. It helps to have Git installed so you can easily review all the changes afterwards. So one significant change is that all wire models are now deferred by default. So you have to ensure that the .live modifier is added to every model if you want to maintain the live effect. So whenever you do a keystroke down, that will send off a request. At the same time, this means that all wire model.defer directives have simply changed to wire model without the .defer. As you can see, this command will automate all the changes. It will go through all your blade files and make the required changes. Okay, let's move on. Another change is that the lazy modifier has been changed to .blur, so simply press enter, and all occurrences will be updated for you. So similar to our models, internal directive is now deferred by default as well. So you need to ensure that it has the .live modifier, and in case you're using this in your JavaScript, you need to make sure that it has a second parameter and that it has been set to true. Again, everything is automated, so simply press enter, and all occurrences will be updated for you. Given the rectangle directive is now deferred by default, we can remove the deferred modifier from all occurrences. The prefetch modifier from the click directive has been removed, so simply press enter and remove all those occurrences. Next, we got livewire load, which has been changed to livewire init. So in version 2, you are probably used to using this.emit to dispatch events or using this.dispatchBrowser events. In version 3, it has all changed. You now use this.dispatch to emit events. So this means all assertion helpers also need to update, and that's what we'll do right now. And please be aware, the concept of emitting events up has been removed. So all automatic replacements have been done, but let's take a look at some steps that you might want to update manually. For example, in version 3, the config has been updated. So if you haven't published it, then it's fine. If you have published your config file, be sure to check and make sure that you update it with all the appropriate new configuration options. And if you're wondering what these new added options are and why some of these options are removed, check out the documentation, and everything is explained there. So another significant change with version 3 is that it now ships with Alpine.js. And this is required for Livewire to function. So in version 2, Alpine.js was not a dependency; now it is. And Alpine.js is bundled with all the Livewire JavaScript as well. So you need to make sure that you remove any instances that you've added yourself. Whether it be a CDN or when you're importing Alpine.js manually, be sure to remove any of these references. The version of Alpine.js that is bundled with Livewire also includes most of Alpine's plugins, meaning intersect, collapse, persist, morph, focus, and mask. So if you were loading any of these plugins manually, you can remove those references as well. In case you were loading Alpine inside your JavaScript itself, like in a using module, in that case, you can use the import function and import Livewire and Alpine as shown here on your screen. You can register any of your custom Alpine plugins and call Livewire.start, which will start Alpine and Livewire. So as mentioned earlier, the concept of emitting events and dispatching browser events have all been consolidated into a single method called dispatch. So you need to make sure that you update all these references. And so emit becomes dispatch, emitTo, emitSelf becomes dispatch as well, but you add a to method or a self method, so you still get the same functionality. And one very significant change is that it now requires named parameters. So here you can see that we're emitting events posts.created with the postID. Now you need to add a named parameter called postID and pass in the postID. That's quite a significant change. So be sure to double-check that you've updated this everywhere. This, of course, also applies to any of the helpers that you were using directly in your blade files, like the emit helper; this has become dispatch as well. And again, the emitUp concept has been removed entirely. So make sure that any of these references are removed, as events are not dispatched as a browser event. And therefore they bubble up by default. And finally, our compiled views are clear. So we just reviewed quite a big chunk of the upgrade process from version 2 to version 3. There are still some things to be automated, but I expect when we're approaching the stable release, more and more of these upgrade steps will be automated. Feel free to submit a PR if you think like, hey, I want to make this specific step automated. So while we're in beta, I highly recommend that you check out the documentation for the upgrade process as well because that's probably going to be more up-to-date than this command.