Modal Component

Learn everything there is to know about the Modal Component.

Wire Elements - Modal Pro

Install the modal base component

Add the component service provider to your config/app.php to ensure the required assets are loaded.

1'providers' => [
2 
3 /*
4 * Laravel Framework Service Providers...
5 */
6 Illuminate\Auth\AuthServiceProvider::class,
7 
8 /*
9 * Package Service Providers...
10 */
11 WireElements\Pro\Components\Modal\ModalServiceProvider::class,
12]

To use the modal component, you must add the modal base component @livewire('modal-pro') to every page of your application where you want to be able to use the modal—for example, resources/views/layouts/app.blade.php

1<!DOCTYPE html>
2<html>
3<head>
4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1">
6 
7 <title>Laravel</title>
8 
9 @livewireStyles
10</head>
11<body>
12 
13 <!-- Require the Modal Pro component -->
14 @livewire('modal-pro')
15 
16 @livewireScripts
17</body>
18</html>

Add the @livewire('modal-pro') directive to your layout.

Compile Javascript

This component requires some additional Javascript so ensure this is compiled by requiring the script in your resources/js/bootstrap.js:

1require('../../vendor/wire-elements/pro/resources/js/overlay-component.js')

To compile you will also need to install the following NPM dependencies:

  • npm i @alpinejs/focus

Make sure you enable the Alpine focus plugin in your resources/js/app.js:

1require('./bootstrap');
2import Alpine from 'alpinejs';
3import focus from '@alpinejs/focus'
4Alpine.plugin(focus)
5window.Alpine = Alpine;
6Alpine.start();

It’s also possible to include the required resources directly; see the following next step.

Include Javascript and CSS directly

You can include Javascript and CSS (Bootstrap only) directly by publishing the package assets. If you are using the Boostrap preset, you must include the additional CSS.

1php artisan vendor:publish --tag=wire-elements-pro-assets
2 
3Copied Directory [/wire-elements-pro/resources/dist] To [/public/vendor/wire-elements-pro]
4Publishing complete.

Publish package assets.

1<!DOCTYPE html>
2<html>
3<head>
4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1">
6 
7 <title>Laravel</title>
8 
9 <!-- Include the overlay-component.css stylesheet -->
10 <link rel="stylesheet" href="{{ asset('vendor/wire-elements-pro/css/bootstrap-overlay-component.css') }}">
11 
12 @livewireStyles
13</head>
14<body>
15 
16 <!-- //.... -->
17 
18 <!-- Include the overlay-component.js script -->
19 <script src="{{ asset('vendor/wire-elements-pro/js/overlay-component.js') }}"></script>
20</body>
21</html>

Make sure to republish assets when you update the package.

Tailwind CSS configuration

If you use Tailwind as your CSS framework, it’s essential to include the Wire Element components in the contents array. Add the following paths to the content array to ensure the required CSS is generated.

1module.exports = {
2 
3 content: [
4 './vendor/wire-elements/pro/config/wire-elements-pro.php',
5 './vendor/wire-elements/pro/**/*.blade.php',
6 ],
7 
8 //....
9};

Configure the tailwind.config.js content array

Chances are you want to make changes to the look and feel of the modal. Publishing the views is one possibility, but staying up to date with any changes will be more time-consuming. For that reason, the Tailwind CSS is in a separate file, so you can make adjustments without the need to edit the blade file. Even if you don't want to make any changes, you must include the CSS like this:

1@tailwind base;
2@tailwind components;
3@tailwind utilities;
4 
5@layer components {
6 @import "../../vendor/wire-elements/pro/resources/css/tailwind/overlay-component.css";
7 
8 // Add your customizations here
9 
10 // Example: Disable the transition animation when reduced motion is enabled
11 .wep-modal-content,
12 .wep-slide-over-container-inner-wrap {
13 @apply motion-reduce:transition-none;
14 }
15}

Add the import statement to your resources/css/app.css

Configuration

If you want to make any configuration changes to the modal component, you will need to publish the config file using the following command:

1php artisan vendor:publish --tag=wire-elements-pro-config
2 
3Copied File [/wire-elements-pro/config/wire-elements-pro.php] To [/config/wire-elements-pro.php]
4Publishing complete.

Publish the Wire Elements Pro configuration file

The config file contains the default modal behavior and attributes. The different behaviors and attributes are explained further along in this document.

Using Bootstrap as your front-end framework

If you are using Bootstrap as your front-end framework, you will have to change the preset from Tailwind to Bootstrap:

1return [
2 'default' => 'bootstrap',
3 
4 // ...
5];

Creating your first modal

You create a Livewire component as you would regularly do to get started. For example, by running the make command:

1php artisan livewire:make UsersOverview
2 
3COMPONENT CREATED 🤙
4 
5CLASS: app/Http/Livewire/UsersOverview.php
6VIEW: resources/views/livewire/users-overview.blade.php

Next, you will need to extend your component with the Modal class instead of the Component class.

1namespace App\Http\Livewire;
2 
3use Livewire\Component; // Remove
4use WireElements\Pro\Components\Modal\Modal;
5 
6class UsersOverview extends Modal
7{
8 public function render()
9 {
10 return view('livewire.users-overview');
11 }
12}

Extend the Modal class instead of the default Livewire component class.

The modal will show the contents of your view. There is no structure applied by default, so you are free to add anything you like. Alternatively, you can use our Blade component:

1<x-wire-elements-pro::tailwind.modal on-submit="save" :content-padding="false">
2 <x-slot name="title">Your Title</x-slot>
3 
4 <!-- No padding will be applied because the component attribute "content-padding" is set to false -->
5 <div>
6 <label>Your email</label>
7 <input type="email" placeholder="[email protected]">
8 </div>
9 
10 <x-slot name="buttons">
11 <button type="submit">
12 Save Changes
13 </button>
14 <button type="button" wire:click="$emit('modal.close')">
15 Cancel
16 </button>
17 </x-slot>
18</x-wire-elements-pro::tailwind.modal>

To get started, wrap your content using the blade component x-wire-elements-pro::tailwind.modal. If you use Boostrap, you need to use x-wire-elements-pro::bootstrap.modal.

This component accepts a few arguments to make things easier. For example, you can use the on-submit attribute to define the method to be called on your Livewire component when the form is submitted.

By default, padding will be applied to the body of the modal. In some cases, you might not want this, for example, when adding a full-width table to your modal. If this is the case, you can add :content-padding="false". To set the title for your modal you must use the title slot.

If you want to display buttons on your modal you can add these to the buttons slot.

Setting behavior and attributes

If you want to change the behavior or attributes for a specific modal, you can create the following static methods on your component class.

1namespace App\Http\Livewire;
2 
3use WireElements\Pro\Components\Modal\Modal;
4 
5class UsersOverview extends Modal
6{
7 public function render()
8 {
9 return view('livewire.users-overview');
10 }
11 
12 public static function behavior(): array
13 {
14 return [];
15 }
16 
17 public static function attributes(): array
18 {
19 return [];
20 }
21}

The behavior and attributes you define on a modal will override the defaults specified in the wire-elements-pro.php config file. Let’s take a look at the available options.

1namespace App\Http\Livewire;
2 
3use WireElements\Pro\Components\Modal\Modal;
4 
5class UsersOverview extends Modal
6{
7 public function render()
8 {
9 return view('livewire.users-overview');
10 }
11 
12 public static function behavior(): array
13 {
14 return [
15 // Close the modal if the escape key is pressed
16 'close-on-escape' => true,
17 // Close the modal if someone clicks outside the modal
18 'close-on-backdrop-click' => true,
19 // Trap the users focus inside the modal (e.g. input autofocus and going back and forth between input fields)
20 'trap-focus' => true,
21 // Remove all unsaved changes once someone closes the modal
22 'remove-state-on-close' => false,
23 ];
24 }
25 
26 public static function attributes(): array
27 {
28 return [
29 // Set the modal size to 2xl, you can choose between:
30 // xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl, 7xl
31 'size' => '2xl',
32 ];
33 }
34}
Context Method
Javascript
1Livewire.emit('modal.open', 'users-overview');
Blade
1<button onclick="Livewire.emit('modal.open', 'users-overview')">
2Open</button>
Inside a Livewire Component View
1<button wire:click="$emit('modal.open', 'show-users')">Open</button>
Inside a Livewire Component Class
1$this->emit('modal.open', 'users-overview');

Passing properties

Often you want to pass properties to your modal, for example, when you want to open a modal to edit a user. You can do this by adding a third argument to the examples listed in the table above.

1class EditUser extends Modal
2{
3 public int|User $user;
4 
5 public function mount(User $user) {
6 $this->user = $user;
7 }
8 
9 public function render()
10 {
11 return view('livewire.edit-user');
12 }
13}

To open this component you would use one of the following ways:

Context Method
Javascript
1Livewire.emit('modal.open', 'edit-user', {'user': 1});
Blade
1<button onclick="Livewire.emit('modal.open', 'edit-user', {'user': 1})">Open</button>
Inside a Livewire Component View
1<button wire:click="$emit('modal.open', 'edit-user', {'user': 1})">Open</button>
Inside a Livewire Component Class
1$this->emit('modal.open', 'edit-user', ['user' => 1]);

Passing modal behavior and attributes

It’s also possible to override the modal behavior and attributes when opening a modal by passing a fourth and fifth argument:

1Livewire.emit('modal.open', 'edit-user', {"user": 1}, {"size":"2xl"}, {"close-on-escape": false});

Closing your modal

To close your modal you can use a similar syntax as opening a modal.

Context Method
Javascript
1Livewire.emit('modal.close');
Blade
1<button onclick="Livewire.emit('modal.close')">Close</button>
Inside a Livewire Component View
1<button wire:click="$emit('modal.close')">Close</button>
Inside a Livewire Component Class
1$this->close();

By default, when you close a child modal (a child modal means you’ve opened a second modal from an existing modal), it will return to the parent modal. Think of this as your browser history; when you close a modal, you are doing the same as going back to the previous page. In some cases, you might not want this behavior. You can pass the force argument to force the closing of all modals.

1Livewire.emit('modal.close', { force: true });

If you don’t want to force all modals to be closed, but instead, you want to remove one or more modals from the history, you can use the andForget parameter.

1class DeleteUser extends Modal
2{
3 public int|User $user;
4 
5 public function mount(User $user) {
6 $this->user = $user;
7 }
8 
9 public function confirmDelete()
10 {
11 $this->user->delete();
12 
13 $this->close(
14 andForget: [
15 EditUser::class,
16 ]
17 );
18 }
19 
20 public function render()
21 {
22 return view('livewire.delete-user');
23 }
24}

This will remove all existing EditUser modal from the history. Suppose you want to be more explicit and only remove the EditUser modal for the user you’ve just deleted. In that case, you can pass the component parameters that must match for the modal to be removed from the history.

1public function confirmDelete()
2{
3 $this->user->delete();
4 
5 $this->close(
6 andForget: [
7 EditUser::class => ['user' => $this->user->id],
8 ]
9 );
10}

Emitting events

When closing your modal, you would likely want to emit events; for example, when you use a modal to delete a user, you wish to update your user overview to ensure the deleted user is no longer listed. Let’s take a look at how we can achieve this.

1class DeleteUser extends Modal
2{
3 public int|User $user;
4 
5 public function mount(User $user) {
6 $this->user = $user;
7 }
8 
9 public function confirmDelete()
10 {
11 $this->user->delete();
12 
13 $this->close(
14 andEmit: [
15 'userDeleted'
16 ]
17 );
18 }
19 
20 public function render()
21 {
22 return view('livewire.delete-user');
23 }
24}

Alternatively, if you want to emit events to a specific component and pass parameters to your event, you can use the following syntax:

1public function confirmDelete()
2{
3 $this->user->delete();
4 
5 $this->close(
6 andEmit: [
7 // Emit globally
8 'userDeleted' => ['param1', 'param2'],
9 // Emit the event to the UsersOverview component
10 UsersOverview::class => ['userDeleted', ['param1', 'param2']],
11 ]
12 );
13}

Listening to events works just like any other Livewire component:

1class UsersOverview extends Modal
2{
3 public function getListeners()
4 {
5 return ['userDeleted' => '$refresh']; // Refresh the component
6 }
7 
8 public function render()
9 {
10 return view('livewire.users-overview');
11 }
12}

Confirmation modal

Wire Elements Pro ships with an opinionated confirmation modal you can use when you want to ask your users for confirmation. All you have to do in order to use this feature is to include the InteractsWithConfirmationModal trait.

1use WireElements\Pro\Concerns\InteractsWithConfirmationModal;
2 
3class UsersOverview extends Modal
4{
5 use InteractsWithConfirmationModal;
6 
7 public function delete($userId)
8 {
9 $this->askForConfirmation(
10 callback: function() use ($userId) {
11 User::find($userId)?->delete();
12 },
13 );
14 }
15 
16 public function render()
17 {
18 return view('livewire.user-overview');
19 }
20}

Easy right? But that's not all, the confirmation modal ships with additional features. Let's take a look what options are available.

1$this->askForConfirmation(
2 callback: function() use ($userId) {
3 User::find($userId)?->delete();
4 },
5 prompt: [
6 'title' => __('Warning! Destructive action'),
7 'message' => __('Are you sure you want to delete this user?'),
8 'confirm' => __('Yes, Delete'),
9 'cancel' => __('Stop'),
10 ],
11 tableHeaders: ['Column 1', 'Column 2'],
12 tableData: [
13 ['Row 1 - Column 1 Value', 'Row 1 - Column 2 value'],
14 ['Row 2 - Column 1 Value', 'Row 2 - Column 2 value'],
15 ],
16 confirmPhrase: 'DELETE',
17 theme: 'warning',
18 metaData: [
19 'custom' => 'meta data'
20 ],
21 modalBehavior: [
22 'close-on-escape' => false,
23 'close-on-backdrop-click' => false,
24 'trap-focus' => true,
25 ],
26 modalAttributes: [
27 'size' => '2xl'
28 ]
29);
  • prompt:
    Override the default confirmation prompt with your own copy.
  • tableHeaders:
    Create a table and set the column headers,
  • tableData:
    Set the table data to display,
  • confirmPhrase:
    The value someone must answer to confirm his/her action.
  • theme:
    This value is not used by default but if you want you could use this parameter to adjust the appearance of the confirmation modal by publishing the view and using $theme to make your changes.
  • metaData:
    This value is not used by default but if you want you could use this parameter to pass any additional data to the confirmation modal.
  • modalBehavior:
    Set the confirmation modal behavior like what happens when a user hits the escape key.
  • modalAttributes:
    Set the confirmation modal attributes like the size.

Please be aware that all these properties are public and can be seen by anyone using devtools.

If you want to customize the confirmation modal you can either publish the views or change the view in the wire-elements-pro.php config file:

1return [
2 'default' => 'tailwind',
3 
4 'components' => [...],
5 
6 'presets' => [
7 'tailwind' => [
8 'modal' => [
9 'size-map' => [...],
10 'confirmation_view' => 'wire-elements-pro::modal.tailwind.confirmation',
11 ]
12 ],
13 'bootstrap' => [
14 'modal' => [
15 'size-map' => [...],
16 'confirmation_view' => 'wire-elements-pro::modal.bootstrap.confirmation',
17 ]
18 ],
19 ],
20]

Please note that the confirmation modal only works if the parent method calling askForConfirmation consists out of values that can be converted into JSON (integers, strings, and arrays):

1use WireElements\Pro\Concerns\InteractsWithConfirmationModal;
2 
3class UsersOverview extends Modal
4{
5 use InteractsWithConfirmationModal;
6 
7 // $userID is an integer or string
8 public function delete($userId)
9 {
10 $this->askForConfirmation(
11 callback: fn() => User::find($userId)?->delete(),
12 );
13 }
14 
15 // Passing an entire object won't work
16 public function delete(User $user)
17 {
18 $this->askForConfirmation(
19 callback: fn() => $user->delete(),
20 );
21 }
22}
To navigate
Press Enter to select
Documentation
/