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

jaumevallsvila's avatar

Sidebar menu headlessui not open after navigate to a page

Hey guys, Don't know what I'm doing wrong with headlessui dialog, the dialog works as expected however when I click on a page section link (dashboard for example) the open behavior stops working until I refresh the page. Below the code and video with wrong behavior

<script setup>
import {Dialog, DialogOverlay, DialogPanel} from "@headlessui/vue";
import {ref} from "vue";
import NavLink from "@/Components/NavLink.vue";

const isOpen = ref(false)

const navItems = [
    {
        label: "Dashboards",
        children: [
            {
                href: "section1", label: "Section1"
            },
        ]
    },
    {
        label: "Management",
        children: [
            {
                href: "section2", label: "Section2"
            },           
        ]
    },
];

let participantRefs = ref([])

const addRef = (el) => {
    if (el) {
        participantRefs.value.push(el);
    }
}
</script>

<template>
    <div>
        <button class="md:hidden text-gray-600" type="button" @click="isOpen = true">
            <span class="sr-only">Navigation</span>
            <svg aria-hidden="true" class="flex-shrink-0 w-6" fill="none" stroke="currentColor"
                 stroke-width="1.5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                <path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round"
                      stroke-linejoin="round"></path>
            </svg>
        </button>
        <Dialog :initial-focus="participantRefs[0]" :open="isOpen" class="relative z-50"
                @close="isOpen = false">
            <div class="fixed inset-0 overflow-y-auto">
                <div class="flex min-h-full">
                    <DialogOverlay class="fixed inset-0  bg-white/25 backdrop-blur"/>
                    <div class="fixed inset-0 overflow-y-auto">
                        <div class="flex min-h-full">
                            <DialogPanel class="bg-white shadow p-6 w-[230px]">
                                <nav class="flex flex-col gap-6">
                                    <div v-for="item in navItems" :key="item.label">
                                        <span class="text-gray-700 font-bold text-sm">{{ item.label }}</span>
                                        <div v-if="item.children.length" class="mt-2">

                                            <NavLink
                                                v-for="child in item.children"
                                                :key="child.label" :ref="addRef"
                                                :active="route().current(child.href)"
                                                :href="route(child.href)"
                                            >
                                                {{ child.label }}
                                            </NavLink>

                                        </div>
                                    </div>
                                </nav>
                            </DialogPanel>
                        </div>
                    </div>
                </div>
            </div>
        </Dialog>

    </div>
</template>

https://anonymfile.com/1q7V/grabacion-de-pantalla-2024-02-10-a-las-74511.mov

0 likes
7 replies
LaryAI's avatar
Level 58

Based on the description of the problem, it seems like the dialog stops working after navigation because the state of the application is not being maintained correctly across route changes. This could be due to the fact that the dialog's state is only managed within the component and not globally or in a way that persists across route changes.

One way to solve this issue is to ensure that the state of the dialog (isOpen) is managed at a higher level, such as within a global state management solution like Vuex or Pinia, or by using the Vue Router's navigation guards to reset the state when the route changes.

Here's a potential solution using Vue Router's navigation guards to reset the dialog state:

<script setup>
import { Dialog, DialogOverlay, DialogPanel } from "@headlessui/vue";
import { ref, onBeforeUnmount } from "vue";
import { useRoute, useRouter } from "vue-router";
import NavLink from "@/Components/NavLink.vue";

const isOpen = ref(false);

const navItems = [
  // ... your navItems array
];

let participantRefs = ref([]);

const addRef = (el) => {
  if (el) {
    participantRefs.value.push(el);
  }
};

const router = useRouter();
const route = useRoute();

// Watch for route changes and close the dialog if it's open
router.afterEach(() => {
  if (isOpen.value) {
    isOpen.value = false;
  }
});

// Clean up the watcher when the component is unmounted
onBeforeUnmount(() => {
  router.afterEach(() => {});
});
</script>

<template>
  <!-- ... your template code -->
</template>

In this solution, we're using the afterEach navigation guard from Vue Router to watch for route changes. When a route change occurs, we check if the dialog is open (isOpen.value) and if it is, we close it by setting isOpen.value to false. We also make sure to clean up the watcher when the component is unmounted to prevent memory leaks.

This should ensure that the dialog behaves correctly even after navigating to a different page. If you're using a global state management solution, you would manage the isOpen state there instead and watch for changes in a similar way.

Remember to import useRouter and useRoute from vue-router if you haven't already done so in your project.

jaumevallsvila's avatar

Try several things without success, handle the state in a @click event passing the state from parent component to the child

Going deeper the issue is related that when page has changed the button that displays the dialog toggle from false to true and from true to false in a single click causing the dialog open and close in the same click

gych's avatar

@jaumevallsvila Its not 100% clear what your exact issue is.

When you navigate to another page the sidenav closes but instead it should stay open?

Then when trying to open the navigation again it opens and closes instantly?

jaumevallsvila's avatar

Hey @gych,

the behavior is like this, the user clicks on the Hamburger icon, the dialog open correctly, when the user click outside the menu the dialog close correctly.

The issue happens when the user click on the Hamburger icon, the dialog shows up, then the user click on a link href, the dialog close the user is redirected to the page, if the user click on the Hamburger icon again the dialog is not shown (well it toggle from true to false in the same click)

hope know is more clar

gych's avatar
gych
Best Answer
Level 29

@jaumevallsvila Try to put a div around the dialog that holds the condition if isOpen

See code below as example

<template>
    <div>
        <button class="md:hidden text-gray-600" type="button" @click="isOpen = true">
            <span class="sr-only">Navigation</span>
            <svg aria-hidden="true" class="flex-shrink-0 w-6" fill="none" stroke="currentColor"
                 stroke-width="1.5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                <path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" stroke-linecap="round"
                      stroke-linejoin="round"></path>
            </svg>
        </button>
		<div v-if="isOpen">
        	<Dialog 
					:initial-focus="participantRefs[0]"
					:open="isOpen" 
					class="relative z-50" 
					@close="isOpen = false"
			>
				<!-- Content of your dialog -->
       		</Dialog>
	</div>
    </div>
</template>
1 like
jaumevallsvila's avatar

@gych You are the best, this div makes the magic, I don't know why but it works, thank you very much

1 like

Please or to participate in this conversation.