Summer sale! Save 50% on access to our entire library of courses.Join here →

Clean Reusable Livewire Modals That You Can Trigger From Anywhere

May 6th, 2021

Everyone loves modals, and with Livewire they're surprisingly easy to implement with the help of Alpine.js. I've been through so many iterations of the perfect modal setup, and *this* is the solution I've arrived at for clean, reusable modals that you can trigger from absolutely anywhere.

This article covers Livewire 2. For the Livewire 3 solution, there's an updated course available here covering everything you need

If you're following along, get yourself a fresh Laravel project set up, install Laravel Breeze for some scaffolding, and install Livewire. This is going to make it really easy to focus on the important bits.

Any modal we create needs the ability to show and hide itself. Rather than duplicate that functionality across multiple Livewire components, start with a base component that you can extend.

Create the component.

php artisan livewire:make Modal

Here's what it needs to look like.

namespace App\Http\Livewire;

use Livewire\Component;

class Modal extends Component
    public $show = false;

    protected $listeners = [
        'show' => 'show'

    public function show()
        $this->show = true;

We'll go into more detail on this later, in particular the $listeners we've added here.

This is the modal that's going to show, so the content is up to you. For this example, let's create a contact us modal.

php artisan livewire:make ContactModal

The code for this is super simple, we just extend the base Modal we created in the last step.

namespace App\Http\Livewire;

use Livewire\Component;
use App\Http\Livewire\Modal;

class ContactModal extends Modal
    public function render()
        return view('');

Now we actually need to design a modal. I've done the hard work for you here, but you might want to tweak this for your own needs.

First, create a Blade component for the modal so we can easily re-use it.

php artisan make:component Modal

And add the following markup. I'm using Tailwind to style it.

<div class="fixed inset-0 overflow-y-auto px-4 py-6 md:py-24 sm:px-0 z-40">
    <div class="fixed inset-0 transform">
        <div class="absolute inset-0 bg-gray-500 opacity-75"></div>

    <div class="bg-white rounded-lg overflow-hidden transform sm:w-full sm:mx-auto max-w-lg">
        {{ $slot }}

The first child div here is the faded background of the modal (note the opacity-75 class) and the second is the actual content of the modal, added via the default Blade component slot.

Now open up the contact-modal.blade.php Livewire view and add the following.

        <div class="p-6">
            Contact modal

If you want to a modal to be global to your entire site, you can add it to your app.blade.php base template. Or, if it belongs in one place only, add it there.

I'm adding it to the bottom of the app.blade.php file.

            {{-- The rest of your app.blade.php file up here --}}
            <livewire:scripts />
            <livewire:contact-modal />

If you refresh now, you should see your modal displayed. Obviously not ideal to have it always display. So, we need to...

Open contact-modal.blade.php and update it to pass through a Livewire attribute.

    <x-modal wire:model="show"> {{-- <-- Add this --}}
        <div class="p-6">
            Contact modal

Now update the modal.blade.php Blade component to take this value and only show the modal if show is true.

If you're confused at this point, we'll run through this shortly.

        show: @entangle($attributes->wire('model')).defer
    x-on:keydown.escape.window="show = false"
    class="fixed inset-0 overflow-y-auto px-4 py-6 md:py-24 sm:px-0 z-40"
    <div x-show="show" class="fixed inset-0 transform" x-on:click="show = false">
        <div class="absolute inset-0 bg-gray-500 opacity-75"></div>

    <div x-show="show" class="bg-white rounded-lg overflow-hidden transform sm:w-full sm:mx-auto max-w-lg">
        {{ $slot }}

Ok, let's break down what's happening.

  1. Our base Modal component has a show property (and thus so does our contact modal, because it extends it).
  2. From the contact modal, we pass in wire:model="show" to tell the Blade component the true/false value of this.
  3. In the modal Blade component, we use Alpine.js to set a property using @entangle. This shares the show property value from the Livewire component with Alpine.js and keeps it in sync.
  4. We add some x-show conditions to parts of the modal so they're only shown if the show property is true.
  5. We add a x-on:keydown.escape.window event handler to set show to false if the escape key is pressed.
  6. We add a x-on:click event handler to set show to false if the faded background of the modal is clicked (anywhere outside the actual modal content).

If you refresh right now, you'll no longer see the modal. Try setting the $show property on the Modal.php Livewire component to true and refreshing again. You should see your modal appear! Don't forget to return it back to false.

Now, like magic, from anywhere in your app, you can show this modal. Here's an example of a button you could press to trigger the contact modal.

<button x-data="{}" x-on:click="window.livewire.emitTo('contact-modal', 'show')" class="text-indigo-500">
    Show contact modal

This emits a show event to the contact modal.

Remember the $listeners from the base Modal Livewire component? This picks up on the event and sets the $show property to true. Because we used @entangle, this keeps in sync with Alpine.js and shows the modal.

You can even trigger the modal directly from another Livewire component.

$this->emitTo('contact-modal', 'show');

The beauty of this solution is that it's quickly reusable. You'll just need to:

  1. Create another Livewire component for your new modal, making sure you extend the base Modal component.
  2. Fill in the Livewire view with the Blade modal component.
  3. Trigger it using the component name.

Although the solution is simple, if you're new to Livewire or Alpine.js this might be a lot to take in. If you'd like to learn more, we have loads of courses that'll get you up to speed.

Happy modaling!

Thanks for reading! If you found this article helpful, you might enjoy our practical screencasts too.
Alex Garrett-Smith
Share :


No comments, yet. Be the first to leave a comment.