Introduction and Demo
There are loads of different ways to create and trigger modals in Livewire, but coming up with a global solution can be a little bit tricky. And in this video, I'm going to simplify this down and create global models that can be triggered from anywhere, even with multiple links. We're not going to specifically tie the showing of modals into any particular Livewire component, which you might have done in the past, or if you're just completely new to triggering modals in Livewire, this should give you a good start.
Let's look at how this works. I've created two global models. One is for a contact page; the link for this will probably live in the footer or somewhere in your app. And then just some other modal. Let's go ahead and click on this, and you can see that that triggers a modal. Sure enough, we can either hit the escape key to get rid of that, or we can just click anywhere outside. We're going to be keeping the actual modal itself pretty simple. You can go ahead and extend this to use any functionality that you need.
So we have this some other modal here as well, which pretty much does the same thing, but it just has different content. If we just look at the class here or the component, the actual Livewire component, you can see it's pretty straightforward. There's nothing in here that's telling us whether to show this model or not. That's all tucked away in this base modal component, which we're going to be extending. And of course, we actually need to trigger this modal. How would we do that?
Well, we could emit an event from a Livewire component, but that would mean that we would have to create a Livewire component and have that sit every place that we want this model to trigger. Instead, what we're going to be doing is doing this with Alpine.js, and we're going to hit Livewire over on the window object and emit to the specific model. So let's go ahead and get started with this. We'll start things off with just a fresh Laravel app with Breeze already installed and of course, Livewire pulled in as well.
Initial Setup and First Modal
Okay, so we're back to a fresh Laravel project with Laravel Breeze installed, just so we have a kind of scaffolding to work with, as well as Alpine.js already pulled in in our head. And we also have Livewire installed as well, of course, because that's what we're covering.
Now, we're going to start things off by creating out a contact component or contact modal component with Livewire. Let's go ahead and kick this off. So let's say livewire:make and let's go ahead and call this ContactModal. Now, by default, of course, what's going to happen here is with our contact model, we're going to return this view. Inside of here, we're going to use that, but we are not going to style the modal up just yet. We kind of want to play around with this first. We'll get to actually styling the modal at the end of the video. But basically inside of here, I just want some text just so I can see this triggered. So let's say just contact modal.
Now we're going to go ahead and place this globally. It doesn't matter where you put this, but I'm going to put this at the bottom of the app.blade.php file, which is the template for our entire app. So let's go ahead and say contact modal and we're pretty much done. That now lives on the page and of course, we can see it just at the bottom down here.
Implementing the Show/Hide Logic
Now what we want to do is create an anchor or a button over here that when we click this, this only shows. And remember, this will actually be the modal eventually when we click that button. To do this, we're going to start things off by creating out a show property which will be by default false. Then in the template itself, we can just wrap this in an if statement. We'll be doing something slightly differently to an if statement a little bit later, but for now, this will kind of prove how we want this to work. So now that we have the condition, of course, that is now not going to be shown. We want it to show when we click on a link over in our dashboard.blade.php view.
Triggering the Modal with Alpine.js
So let's go ahead and create out a button just inside of here and let's say Show contact modal. And let's go ahead and use Alpine.js here. So let's initialize this with x-data just inside of here. And basically, when we click on this, we want to emit an event which will go ahead and set the show property to true.
So for that, let's go ahead and first of all, create some kind of setter in here, and let's set this show to true. Okay, so what we now need to do is from our dashboard emit an event globally within Livewire. Now to do this, we just use window.livewire, we say emitTo, we choose the component, which in our case is contact-modal, and then we just choose the name of the event that we want to emit or the value or key of the event that we want to emit. So I'm just going to say show for now, keep this really simple.
Now, over in our ContactModal, that's not going to do anything at the moment. It's not going to trigger this method. So I'm going to go ahead and add in a listener in here. So let's go ahead and say listeners and let's define out our show listener. And we've got the same method name, so we don't necessarily need to do this, but let's go ahead and just target show. So now what we're doing is when we click on this button, we're triggering and emitting that show event over on ContactModal, that will be picked up here. It will then show the modal and we should see it.
Refactoring into a Base Modal Component
Let's go ahead and just add a little bit of styling to this just to make it look more like a link. So I'm going to set the text to indigo-500. And let's go over and click on this. Remember, at the moment, it's not visible at the bottom of the page. When I click on this, sure enough, it has been triggered. So when we get round to actually creating the modal itself, of course, it won't be shown at the bottom here, it will actually show with a full screen with that background in.
Now, the only problem we've got is we want this to be really flexible. We don't want to create another model and have to do all of this over again. So what we're going to do is create another modal, copy that over, see the problems that we're going to get, and then we'll go ahead and simplify this to make it much more reusable. Because the key is if you need another modal, you want to be able to quickly create it. So let's go ahead and again use Livewire to make out SomeOtherModal. I can't really think of any examples at the moment, and let's just wait for that to come in. And let's just copy pretty much everything we have over here, just so we can prove that this works in exactly the same way.
So over in our ContactModal, well let's grab the code that we've got for this, pretty much paste it over here and say some other modal. So it works exactly the same way. And then over in our dashboard, let's go ahead and just duplicate this button. I'm just going to wrap this in a div just so it comes down a little bit. Let's just pull that in. And why don't we just copy this entire block and just pop it down here? So show some other modal and of course here we want to reference some-other-modal. It's pretty much all we need to do. So now we've got two links. When we click show some other modal, that should trigger it. And it doesn't look like it does. Let's have a look. Uh, yeah, of course, we didn't actually add it to the bottom of our page. So let's do that first of all. If it doesn't exist, we can't do anything with it. So some-other-modal. And let's go over and try that again. So click that, that triggers some other modal. Click the contact one, that triggers the contact modal. Great. Now, there's a massive common thing between these two, and that is all of our show functionality. So what we can pretty much do is copy this over to a base Livewire component. Let's go ahead and do that now.
So let's go ahead and just make out another component called Modal. There are lots of different ways that we can do this, but let's just keep this really simple. Now, this by default has a render method. We don't actually need to render anything out for this because it's just a base component. So let's go ahead and just get rid of that and then we'll get rid of the modal.blade.php file as well, which actually is in resources/views/livewire. So let's delete that. There we go. Okay, great. So now we can pretty much take all of this, get rid of that, pop it in the base here, and do the same for our contact modal, but then extend that base model. So let's import that here. And let's import that over here as well. So now when we want to create a new model, all we need to do is create this and extend the base model. And then we just have a really convenient way of triggering it directly from here. Let's go over and just try this out. Show contact, show some other model. So that works really nicely.
Creating a Reusable Blade Component for UI
But of course, we actually want a modal window in here. So let's implement a really simple version of this and then you can go ahead and improve on this if you need to. So for this, we're not going to create a Livewire component. There's no need to do that. We can just use a Blade component. Let's go ahead and say php artisan make:component and let's make out just a Modal component.
Now we don't need the class for this, so we can come over to view and just get rid of that in there. And we can go over to modal.blade.php, and we can go ahead and start to fill this in. So I'm just going to say modal for now. And what we're going to do with this component is over in our contact modal for Livewire, we're going to go ahead and add this into here. So we're just going to say <x-modal>, and then anything inside of here will be shown. So contact modal.
Let's do the same thing as well for our SomeOtherModal. And notice we're removing if show. Now the reason that we're doing that is we're going to entangle inside of our modal the show property from our base modal component. So we're going to use this value to determine inside of here whether to actually show this modal component. So at the moment, it just looks like this where we have these two on the page down here. So let's go ahead and just start to style the modal up, and then we'll look at triggering it to show it.
Styling the Modal Component with Tailwind CSS
Okay, so the overall class for this, let's go ahead and set to fixed and inset-0. This is Tailwind styling, if you're not familiar. And we're going to go ahead and set the overflow on the y-axis to auto. Let's add some padding in here. So let's say padding on the x of 4, padding on the y of 6, and on medium, let's set the padding to 24 and on small, padding on the x-axis to zero. Let's give this a slightly higher z-index as well. So this is the overall wrapper. If we just look at this now, it just kind of fills up this entire thing. Let's go ahead and inspect this and you can see, sure enough, it just fills up the entire page, but with that padding in there.
So now we want the kind of background to this. So let's go ahead and create this out in a separate div. And let's again make this fixed and inset-0 so it fills up the entire screen. And let's set transform on this as well. Now, inside of this, we're then going to have an absolutely positioned div. Let's make this absolute and again, inset-0 to fill that. And let's set the background, say, gray-500 and let's set the opacity to say, let's go 75. So let's have a look at that and we should see, there we go. So that's the kind of backdrop of our modal.
Now down here is going to be the actual wrapper itself. So of course, this is going to have some kind of white background. Perhaps we can make it rounded-large, overflow can be hidden, and let's say transform as well. And on a small viewport, let's make the width full, and then we'll also make the margin x auto from small, and we'll set a max-width to large as well. Again, it doesn't matter how you style this up. You can go ahead and change this around. Let's give that a refresh and there's our modal. So what we can now do is slot in the content in here, and you'll notice that when we give that refresh, we've got our contact modal content that's coming directly from inside of here to here. So for each of these models, let's just give this a little bit of a padding. I like to kind of leave these open so we can style them directly within here. So let's do that for some other model as well. And that's pretty much the styling done.
Connecting Livewire State with Alpine and @entangle
But of course, it's always on the page because it's over in app.blade.php. We need to work out how to only show this when the show property on either of these modals is set to true. Now, we're going to use Alpine.js again for this. So let's just pull this down, and let's set x-data here to have a show property. So what we can do is set this to false here, and we can only show these two things if that's set to true. So we can say x-if or x-show probably, and we can reference show. So we'll only show this and this when show is true. Of course, when we refresh, both of them are hidden.
But we want this value to come from the base modal component, either on the contact modal or the some other modal. So for this, we can conveniently use @entangle, and what we want to do with this is pass through the value that we're looking for. So over here, when I define <x-modal>, we want to say something like wire:model="show" to reference the show model or show property that exists within there. That value, e.g., true or false, will then get passed down and we can use that inside of here. And we can actually use on our Blade component attributes->wire('model'). So let's go ahead and just try this out. I'm going to go ahead and set this to show and this to show and that should be enough now to get that working. So this value will come directly from here and it will show it or hide it.
Implementing Close Functionality and Optimization
Now let's go over and just try this. Okay, so I've said modal, this needs to be model because we're treating that as a Livewire model. Give that a refresh. Click show contact model... and it's actually not letting me click through at the moment because I believe the modal might be in the way. So let's just have a look at what is not letting us get through here. Okay, so of course, we need x-show on the overall wrapper as well because otherwise this full width and height thing is getting in the way. Let's give that another refresh and click on Show contact modal. That works. We can't click away at the moment. We've not implemented anything to actually close this, so I'm going to have to refresh the page and then click on this one. And you can see that that doesn't actually work. So this is triggering the contact model as well. So let's figure out why that is as well. It might just be because... yeah, we didn't actually change the text over. So Some other modal. And let's go over and click Some other modal. That works nicely. Let's refresh the page, and there we go. So both of them modals are now being triggered successfully.
But of course, we need a way to close these off. So let's go ahead and add an event listener here for keydown.escape.window and let's go ahead and say show to false if that's the case. And that should be pretty much it. But of course, what we want to do is for this kind of outer wrapper, this dark background, that would be a good place to close this as well. So let's go ahead and say x-on:click and let's say show to false here as well. Great. Okay, let's try this out. Let's click show contact model. I can click outside. I can also press the escape key. Of course, for both of these, because they work in exactly the same way.
Now, one more thing, if we just check out the network requests, obviously, because this is a Livewire component, we're making a request to update that property. Now when we close the model, notice we're making another request. This isn't really necessary, so the best thing to do here would be to add a .defer just onto the end of here. Notice that now when we click it, of course, it does make a request to update that, which it needs to do, but when we click away now, it's deferred. So we don't make another request. So no point making two requests where we can just make one.
Final Demonstration and Conclusion
Now the great thing about this solution is that now we can pretty much use this button anywhere we want to trigger this model. We don't—we're not limited to one place. So this isn't tied into a Livewire component, which is what I was talking about at the start of the video. So for example, if we wanted this over in our kind of overall layout, maybe in a footer somewhere, we could just really easily do this. So let's just kind of pretend that we've got a footer element down here with the trigger for Show some other modal. Now that's now going to work from here, but it's also going to work from here. So we can pretty much trigger this global model from anywhere we want.
Now, in terms of the modal styling, this isn't perfect. If you start to introduce forms into this, you may come up with some issues. What I'd recommend you do is actually head over to the Laravel Jetstream source and go ahead and steal the modal code from there because that introduces lots of additional functionality and it's a really solid modal implementation. This is just very, very basic to get us started. So there we go. Global modals which we can just really easily create. Pretty much the whole process of this is create out the modal component. Inside of the view, very, very simple now because we've extracted this out to a Blade component. And then just add in your content and trigger it from absolutely anywhere.