Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

vincent15000's avatar

Package to use InertiaJS to open a page in a modal window

Hello,

Who has tested this package ? It seems to be very interesting to load a route in a modal window.

https://github.com/protonemedia/inertia-vue-modal-poc

If anyone can share its experience about this package ;).

Thanks.

V

0 likes
11 replies
vincent15000's avatar

@Sinnbeck Oh thank you it looks interesting, I will test it. Furthermore it is headless and it doesn't seem to need InertiaJS SSR (whereas the other one --- inertia-vue-modal-poc --- needs SSR).

vincent15000's avatar

@Sinnbeck I have installed the package, the route works fine, but the modal window doesn't display.

I use the el-dialog component (from the Element Plus framework).

I try to open my modal formby clicking on the Edit button in the states datatable.

Inertia.visit(route('states.edit', stateId))

My controller.

public function edit(State $state)
{
    $states = State:all();
    return Inertia::modal('States/Form')->with(compact('state'))->baseRoute('states.index', $states);
    return $state;
}

And the template of my modal form.

<el-dialog
  :title="title"
  v-modal="dialogVisible"
  width="50%"
	@open="loadData()"
	:close-on-click-modal="false"
	destroy-on-close>

  <el-form
  	ref="form"
  	:model="state"
  	label-position="right"
  	label-width="160px">

	  <el-form-item label="Nom">
	    <el-input v-model="state.name"></el-input>
	  </el-form-item>

	  <el-form-item label="Couleur">
	    <el-color-picker v-model="state.color" />
	  </el-form-item>

	</el-form>

  <template #footer>
    <span class="dialog-footer">
      <el-button @click="close()">Annuler</el-button>
      <el-button type="primary" @click="save()">Enregistrer</el-button>
    </span>
  </template>

</el-dialog>

Even if (for the moment) it doesn't work / display, I think that this package doesn't allow to have a real modal window. By reading the documentation, I understand that the page loads the modal window and the background isn't real and is only a kind of image of the datatable or any back page. But perhaps I am wrong.

Sinnbeck's avatar

@vincent15000 sadly I have never used the package as I use react instead of vue

Personally I just use regular modals so I don't have any experience with other solutions

1 like
Namsir's avatar

@vincent15000 You need to wrap your form inside a Modal from the package.

import { Modal } from 'momentum-modal'
mnplus's avatar

I don't know if this is too late, but the package has a sort of a bug, that for some reason you can't access data as a prop, but you can access it differently, same as you would do with inertia-shared data, usePage().props.value.modal.props.{name of the prop}

I think the plugin is very interesting but, it is a bit of a mess. For example, has with() to use, and in the provider, it is not returned, therefore not even used. So the issue is on the part of laravel implementation and not vue. Although for what is worth it so far is the best solution for this type of operation. I mean besides working with API-s.

1 like
vincent15000's avatar

@mnplus I have decided to use simple axios queries to save / update / destroy my models, so I don't use InertiaJS for my modal views.

1 like
alexleo's avatar

Here's simple implementation using InertiaJs shared data and Headless UI component

In your HandleInertiaRequests

  public function share(Request $request): array
    {
        return array_merge(parent::share($request), [
            'flash' => [
                'title' => fn() => $request->session()->get('title'),
                'description' => fn() => $request->session()->get('description'),
            ],
        ]);
    }

Then place your modal in your Layout file (eg AppLayout.vue)

<template>
    <div class="fixed inset-0 flex items-center justify-center">
        <button
            type="button"
            @click="openModal"
            class="rounded-md bg-black/20 px-4 py-2 text-sm font-medium text-white hover:bg-black/30 focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75"
        >
            Open dialog
        </button>
    </div>
    <TransitionRoot appear :show="isOpen" as="template">
        <Dialog as="div" @close="closeModal" class="relative z-10">
            <TransitionChild
                as="template"
                enter="duration-300 ease-out"
                enter-from="opacity-0"
                enter-to="opacity-100"
                leave="duration-200 ease-in"
                leave-from="opacity-100"
                leave-to="opacity-0"
            >
                <div class="fixed inset-0 bg-black/25" />
            </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"
                        enter="duration-300 ease-out"
                        enter-from="opacity-0 scale-95"
                        enter-to="opacity-100 scale-100"
                        leave="duration-200 ease-in"
                        leave-from="opacity-100 scale-100"
                        leave-to="opacity-0 scale-95"
                    >
                        <DialogPanel
                            class="w-full max-w-md 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"
                            >
                                {{ title }}
                            </DialogTitle>
                            <div class="mt-2">
                                <p class="text-sm text-gray-500">
                                    {{ description }}
                                </p>
                            </div>

                            <div class="mt-4">
                                <button
                                    type="button"
                                    class="inline-flex justify-center rounded-md border border-transparent bg-blue-100 px-4 py-2 text-sm font-medium text-blue-900 hover:bg-blue-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
                                    @click="closeModal"
                                >
                                    Got it, thanks!
                                </button>
                            </div>
                        </DialogPanel>
                    </TransitionChild>
                </div>
            </div>
        </Dialog>
    </TransitionRoot>
</template>

<script setup>
import { ref } from "vue";
import {
    TransitionRoot,
    TransitionChild,
    Dialog,
    DialogPanel,
    DialogTitle,
} from "@headlessui/vue";

defineProps({
    title: String,
    description: String,
});

const isOpen = ref(true);

function closeModal() {
    isOpen.value = false;
}
function openModal() {
    isOpen.value = true;
}
</script>

and in your controller

 {
        if ($role->name === 'Super Admin') {
            session()->flash('flash.banner', 'The Super Admin" role cannot be deleted');
            session()->flash('flash.bannerStyle', 'error');
            return redirect()->back()->with('modal', 'The "Super Admin" role cannot be deleted.')->with('description', 'Super Admin role cannot be deleted.');
        }
        $role->delete();
        return redirect()->back()->with('status', 'Role deleted successfully');
    }
1 like

Please or to participate in this conversation.