The Easiest Way to Add Modals to Livewire

February 27th, 2025 • 4 minutes read time

Adding modals to your Livewire applications doesn't have to be complicated. Here's a tried and tested package I always reach for.

In almost every Livewire course I produce that requires modals, I always reach for the wire-elements/modal package.

In this article, I'll guide you through the setup process and the most important elements to have you up and running with Livewire modals in no time!

First up, install the package with Composer:

composer require wire-elements/modal:^2.0

Then, add the modal directive to your base template.

<html>
    <body>
        <main>
            {{ $slot }}
        </main>

        @livewire('wire-elements-modal')
    </body>
</html>

That's all you need to do. By default, this package uses Tailwind CSS to style the modal component. If you're using another CSS framework (or none), you can publish the modal template and tweak it as you need:

php artisan vendor:publish --tag=wire-elements-modal-views

So, installation was easy — how about creating a modal component?

Start by creating a Livewire component in the standard way:

php artisan make:livewire Modals//SomeModal

I put all my modals in a single directory so they're easily reachable.

Open up that component and swap out what it extends to ModalComponent instead of the default Livewire Component:

use LivewireUI\Modal\ModalComponent;

class SomeModal extends ModalComponent
{
    public function render()
    {
        return view('livewire.modals.some-modal');
    }
}

You'll be pleased to know that's it for creating a new modal component!

Just before we trigger the modal, add some content to some-modal.blade.php so we can see something when we do trigger it.

<div>
    I'm a modal
</div>

Now for the fun part — triggering modals.

Let's look at how to do this from Livewire and Alpine to cover both bases.

Here's a sample button that triggers the modal with Livewire:

<button wire:click="$dispatch('openModal', { component: 'modals.some-modal' })">
    Open modal
</button>

If you use $dispatch here within a Livewire component, your modal should now display!

Again, here's a sample button that triggers the modal we've created, this time with Alpine:

<button
    x-data
    x-on:click="Livewire.dispatch('openModal', { component: 'modals.some-modal' })"
>
    Open modal
</button>

Once again, this will open up the modal we created earlier.

You now have two strategies for opening modals depending on where you are in your Livewire application, either inside or outside a Livewire component.

Modals can be useful on their own, but at some point, you will need to pass some data to them.

Here's a simple example of passing a string to the modal and displaying it. First, add a property to hold the value you'd like to pass to the modal:

class SomeModal extends ModalComponent
{
    public string $message;

    public function render()
    {
        return view('livewire.modals.some-modal');
    }
}

Output the value in the modal's template:

<div>
    {{ $message }}
</div>

And then trigger the modal (either from Livewire or Alpine), passing through the $message value:

<div>
    <button wire:click="$dispatch('openModal', { component: 'modals.some-modal', arguments: { message: 'Yo modal' } })">
        Open modal
    </button>
</div>

You can pass as many arguments as you want/need.

Be careful here, and consider how much data you're passing along. For example, imagine we want to pass an entire User through as an argument — not a great idea.

Instead, we'll let this package handle it and look up the typed Modal.

Here's the entire process, starting with a typed User:

class SomeModal extends ModalComponent
{
    public User $user;

    public function render()
    {
        return view('livewire.modals.some-modal');
    }
}

Update the template to access the automatically looked-up model:

<div>
    {{ $user->name }}
</div>

And then trigger the Modal, passing through the ID of the user:

<div>
    <button wire:click="$dispatch('openModal', { component: 'modals.some-modal', arguments: { user: 1 } })">
        Open modal
    </button>
</div>

Of course, you're unlikely to hardcode these. Here's an example of fetching this from an existing variable:

<div>
    <button wire:click="$dispatch('openModal', { component: 'modals.some-modal', arguments: { user: {{ $user->id }} } })">
        Open modal
    </button>
</div>

Let's add a button within our modal to close it off. I'll show you how to close off a modal from a Livewire component shortly.

<div>
    <div>{{ $user->name }}</div>
    <button wire:click="$dispatch('closeModal')">
        Close
    </button>
</div>

Dispatching the closeModal event will close the modal.

If you need to close the modal from your Livewire component (e.g. after an action has taken place), you can do that too:

class SomeModal extends ModalComponent
{
    public User $user;

    public function someAction()
    {
        // do something here

        $this->closeModal();
    }

    public function render()
    {
        return view('livewire.modals.some-modal');
    }
}

There's more you can do with this package — it's really powerful.

The entire documentation is available on the GitHub page for wire-elements/modal.

If you found this article helpful, you'll love our practical screencasts.
Author
Alex Garrett-Smith
Share :

Comments

No comments, yet. Be the first!