Skip to content
Livewire

How to Build a Livewire Modal Dialog (Reusable and Accessible)

3 min read

A modal dialog is one of those components everyone needs and almost everyone re-implements badly. Get it wrong and you ship a div that traps no focus, ignores the Escape key, and is invisible to screen readers. This guide shows how to build a modal in Livewire the right way — reusable, accessible, and animated — using Blade and Alpine.js, with zero JavaScript framework code.

The two layers of a good modal

A modal has two concerns:

  1. State — is it open? In a Laravel app that state usually lives on the server (a Livewire property) or in the DOM (an Alpine boolean).
  2. Behaviour — focus trapping, Escape-to-close, click-outside-to-close, scroll locking, transitions, and the right ARIA roles.

The behaviour is the hard part, and it is identical for every modal. So the smart move is to write it once and reuse it.

Using a ready-made component

The Aura UI modal component packages all of that behaviour. You control the state; it handles the rest:

<x-aura::modal wire:model="showModal">
    <x-aura::modal.header>Confirm deletion</x-aura::modal.header>
    <x-aura::modal.body>
        Are you sure you want to delete this record? This cannot be undone.
    </x-aura::modal.body>
    <x-aura::modal.footer>
        <x-aura::button variant="ghost" x-on:click="$wire.showModal = false">Cancel</x-aura::button>
        <x-aura::button variant="danger" wire:click="delete">Delete</x-aura::button>
    </x-aura::modal.footer>
</x-aura::modal>

On the Livewire side, it is just a boolean:

public bool $showModal = false;

public function confirmDelete(int $id): void
{
    $this->deletingId = $id;
    $this->showModal = true;
}

public function delete(): void
{
    Record::find($this->deletingId)?->delete();
    $this->showModal = false;
}

Because the modal binds with wire:model, opening and closing it is a server-aware boolean — perfect when the modal's contents depend on data you loaded in a Livewire action.

Why accessibility matters here

A correct modal moves focus into the dialog when it opens, traps Tab within it, returns focus to the trigger when it closes, and exposes role="dialog" and aria-modal="true" to assistive tech. The Aura modal does all of this out of the box and is part of a WCAG 2.1 AA-audited component set. Re-implementing it per project is exactly the kind of wheel you should not reinvent.

Putting it in context

Modals rarely live alone — they sit inside forms, tables and dashboards. If you want to see one wired into a real interface, the free dashboard starter shows modals alongside tables, charts and forms, all built from free components. Install Aura UI, drop in the modal, bind a boolean, and you have an accessible dialog in two minutes instead of two hours.

composer require bluestarsystem/aura-ui
php artisan aura:install

Enjoyed this? Get the next one

Practical Laravel UI tips and new components, straight to your inbox. Free, no spam.