emargareten / inertia-modal
Inertia Modal is a Laravel package that lets you implement backend-driven modal dialogs for Inertia apps.
Requires
- php: ^8.2
- illuminate/support: ^11.0|^12.0|^13.0
- inertiajs/inertia-laravel: ^3.0.3
Requires (Dev)
- larastan/larastan: ^3.1.0
- laravel/pint: ^1.21.0
- orchestra/testbench: ^9.2|^10.0|^11.0
- phpunit/phpunit: ^11.5|^12.0
- dev-master
- v3.x-dev
- v3.2.0
- v3.1.0
- v3.0.0
- v2.0.0
- v1.7.0
- v1.6.0
- v1.5.0
- v1.4.0
- v1.3.0
- v1.2.0
- v1.1.0
- v1.0.4
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- v0.1.2
- v0.1.1
- v0.1.0
- v0.0.5
- v0.0.4
- v0.0.3
- v0.0.2
- v0.0.1
- dev-dependabot/github_actions/actions/checkout-6
- dev-dependabot/github_actions/dependabot/fetch-metadata-3.1.0
- dev-add-claude-github-actions-1774529628491
This package is auto-updated.
Last update: 2026-06-03 10:39:05 UTC
README
Inertia Modal is a Laravel package that lets you implement backend-driven modal dialogs for Inertia apps. With this package, you can define modal routes on the backend and dynamically render them when you visit a dialog route.
Note
This package supports Laravel 11+, PHP 8.2+, Inertia Laravel v3, and Vue 3 only.
Note
Inertia Modal targets Inertia v3 and uses Inertia's built-in HTTP client. No separate Axios setup is required.
Installation
Install the Laravel package with Composer:
composer require emargareten/inertia-modal
Install the frontend peer dependencies in your application if they are not already present:
npm install @inertiajs/vue3 vue
Frontend Setup
Modal Component
Modal is a headless component, meaning you have full control over its look, whether it's a modal dialog or a slide-over panel. You are free to use any 3rd-party solutions to power your modals, such as Headless UI.
Put the Modal component somewhere within the layout.
<script setup> import { Modal } from '../../vendor/emargareten/inertia-modal' </script> <template> <div> <!-- layout --> <Modal /> </div> </template>
Note
Ensure that the layout remains persistent throughout the entire application. If you have multiple layouts, create a base layout that should invoke the modal, using nested layouts.
Plugin
Set up the modal plugin with the same component resolver you use to render Inertia pages.
Vite / Laravel
import { createInertiaApp } from '@inertiajs/vue3' import { createApp, h } from 'vue' import { modal } from '../../vendor/emargareten/inertia-modal' import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers' const pages = import.meta.glob('./Pages/**/*.vue') createInertiaApp({ resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, pages), setup({ el, App, props, plugin }) { createApp({ render: () => h(App, props) }) .use(plugin) .use(modal, { resolve: (name) => resolvePageComponent(`./Pages/${name}.vue`, pages), }) .mount(el) } })
If you prefer an alias, point it at the Composer-installed package:
// vite.config.js import { defineConfig } from 'vite' import path from 'node:path' export default defineConfig({ resolve: { alias: { 'inertia-modal': path.resolve('vendor/emargareten/inertia-modal'), }, } })
Usage
Modals have their own routes, letting you access them even via direct URLs. Define routes for your modal pages.
// background context / base page Route::get('users', [UserController::class, 'index'])->name('users.index'); // modal route Route::get('users/{user}', [UserController::class, 'show'])->name('users.show');
Render a modal from a controller. Specify the base route to render the background when the modal is accessed directly.
use Emargareten\InertiaModal\Modal; use Inertia\Inertia; class UserController extends Controller { // ... public function show(User $user): Modal { return Inertia::modal('Users/Show', ['user' => $user])->baseRoute('users.index'); } }
The component argument follows Inertia's component conventions, including configured component transformers and string-backed enums.
Dot notation is supported for modal props:
return Inertia::modal('Users/Show', [ 'user' => $user, 'filters.search' => request('search'), ])->baseRoute('users.index');
Inertia v3 props
Modal responses are resolved through Inertia's v3 response pipeline, so modal props can use the current Inertia prop helpers:
return Inertia::modal('Users/Show', [ 'user' => $user, 'stats' => Inertia::defer(fn () => $user->stats()), 'comments' => Inertia::merge($user->comments()->latest()->get()) ->append() ->matchOn('id'), 'actions' => Inertia::once(fn () => ActionResource::collection($actions)), ])->baseRoute('users.index');
This also preserves Inertia metadata such as shared props, deferred props, merge props, deep merge props, match-on strategies, once props, rescued props, and infinite scroll metadata when a modal opens over an existing page.
Partial reloads for modal props are supported using the nested modal.props.* path. This is also what Inertia uses when loading deferred modal props:
router.reload({ only: ['modal.props.stats'] })
Partial modal responses stay sparse so Inertia can apply mergeProps, prependProps, and deepMergeProps itself. For example, a deferred modal prop using Inertia::merge(...)->append() will be appended by Inertia's native client merge logic, not pre-merged by this package.
If you need to exclude expensive shared props from modal-only responses, publish the config file and update exclude_shared_props:
php artisan vendor:publish --provider="Emargareten\InertiaModal\InertiaModalServiceProvider"
Backdrop behavior
By default, the backdrop page is preserved with its current data while the modal is open. This keeps modal visits fast and avoids reloading the page behind the dialog. When the modal is closed through redirect(), Inertia visits the base URL again.
When a modal URL is opened directly, the configured base URL is dispatched through Laravel's normal router pipeline. Route middleware, model binding, route events, and response preparation run as they would for a normal request to the base page.
If your backdrop needs fresh data while the modal opens, call refreshBackdrop():
public function show(User $user): Modal { return Inertia::modal('Users/Show', ['user' => $user]) ->baseRoute('users.index') ->refreshBackdrop(); }
To ignore the current modal redirect header and force a specific base route as the backdrop, call forceBase():
public function show(User $user): Modal { return Inertia::modal('Users/Show', ['user' => $user]) ->baseRoute('users.index') ->forceBase(); }
This will force re-render of the base route (or even redirect to a different base route).
Both refreshBackdrop() and forceBase() accept a boolean.
Frontend implementation
Use the useModal() composable in your modal component.
This example is a simple headlessui modal, you can add more transitions etc. see https://headlessui.com/vue/dialog.
<template> <TransitionRoot appear as="template" :show="show"> <Dialog as="div" class="relative z-10" @close="close"> <TransitionChild @after-leave="redirect" as="template"> <div class="fixed inset-0 bg-black/75 transition-opacity" /> </TransitionChild> <div class="fixed inset-0 overflow-y-auto"> <div class="flex min-h-full items-center justify-center p-4 text-center"> <TransitionChild as="template"> <DialogPanel class="w-full max-w-lg transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all"> <DialogTitle as="h3" class="text-lg font-medium leading-6 text-gray-900"> <slot name="title" /> </DialogTitle> <slot /> </DialogPanel> </TransitionChild> </div> </div> </Dialog> </TransitionRoot> </template> <script setup> import { TransitionRoot, TransitionChild, Dialog, DialogPanel, DialogTitle } from '@headlessui/vue' import { useModal } from '../../vendor/emargareten/inertia-modal' const { show, close, redirect } = useModal() </script>
The redirect method will redirect to the base route, you can pass in all inertia visit options as a parameter.
redirect({ preserveScroll: true })
The close method will close the modal without redirecting to the base route.
Note
If you configured the Vite alias shown above, import from inertia-modal instead of the vendor path:
import { useModal } from 'inertia-modal'
Testing
composer test
npm run build
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
This package was highly inspired by momentum-modal
License
The MIT License (MIT). Please see License File for more information.