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

eggplantSword's avatar

Headless UI Dialog not displaying

I can't get this dialog to work at all, this is my code

//MODAL.VUE
<script setup>
import { ref } from 'vue'
import {
    TransitionRoot,
    TransitionChild,
    Dialog,
    DialogOverlay,
    DialogTitle,
} from '@headlessui/vue'

const props = defineProps({
    show: {
        type: Boolean,
        default: false
    }
})

const isOpen = ref(props.show);

const closeModal =  function() {
    isOpen.value = false
}
</script>
<template>
    <TransitionRoot appear :show="isOpen" as="template">
        <Dialog as="div" @close="closeModal">
            <div class="fixed inset-0 z-10 overflow-y-auto bg-gray-800 bg-opacity-50">
                <div class="min-h-screen px-4 text-center">
                    <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">
                        <DialogOverlay class="fixed inset-0" />
                    </TransitionChild>
                    <span class="inline-block h-screen align-middle" aria-hidden="true">&#8203;</span>
                    <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">
                        <div
                            class="inline-block w-full max-w-md p-6 my-8 overflow-hidden text-left align-middle
                            transition-all transform bg-white shadow-md rounded">
                            <slot></slot>
                        </div>
                    </TransitionChild>
                </div>
            </div>
        </Dialog>
    </TransitionRoot>
</template>

INDEX.VUE
<script setup>
import BreezeAuthenticatedLayout from '@/Layouts/Authenticated.vue';
import {reactive, watch, ref, defineComponent} from "vue";
import Modal from '@/Components/Modal';

let show_form = false;

const openModal = () => {
    show_form = true;
}
</script>

<template>
    <BreezeAuthenticatedLayout>
        <button @click="openModal">Create</button>
     
        <modal :show="show_form">
            <template v-slot:default>
                <h1>
                    Modal Dialog
                </h1>
            </template>
        </modal>    
    </BreezeAuthenticatedLayout>
</template>

What am I doing wrong? I tried with the approach on the example but I couldn't get that to work either so I was trying to a prop but I haven't had any luck.

0 likes
4 replies
ramonrietdijk's avatar

In your index.vue you are binding show_form to the modal. However, it is not reactive. You should also use ref() for your show_form variable.

eggplantSword's avatar

@ramonrietdijk I tried this but sill nothing happens with the Modal

let show_form = ref(false);

Edit: After some more digging I found out that the const isOpen in Model.vue isn't changing from the original value of false even when the prop show is changing.

ramonrietdijk's avatar
Level 30

@msslgomez Exactly, props only have a one-way data flow. Having 2 different booleans to control the state is not recommended.

As the property show in your modal.vue is responsible to display your modal, I would recommend removing the isOpen ref.

To close the modal, you should use an emit. This way, you will be able to listen to the close event in index.vue to disable the modal again.

// modal.vue
<script setup>
const emit = defineEmits([
    'close',
])
</script>

<template>
    <TransitionRoot appear :show="show" as="template">
        <Dialog as="div" @close="emit('close')">
            <!-- ... -->
        </Dialog>
    </TransitionRoot>
</template>
// index.vue
<script setup>
const closeModal = () => {
    show_form = false;
}
</script>

<template>
    <modal :show="show_form" v-on:close="closeModal()">
        <!-- ... -->
    </modal>
</template>
eggplantSword's avatar

@ramonrietdijk I ended up doing exactly what you're suggesting here! I gave up with trying isOpen and just did an emit to close. Thanks for your answer.

1 like

Please or to participate in this conversation.