Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Headless UI - Popover - Menu item state

Tags:

next.js

I'm trying to implement Popover from https://headlessui.dev/react/popover, but I cannot quite figure out how I can get the menu to close when clicking the menu items.

I'm using NextJS and Link, and that is where I see the behavior. When I am only using < a > tags, then it reloads the page and the menu is closed on reload, but I would like to leverage Link from NextJS.

This is my header file simplified without class names:

import React, { Fragment, useState } from "react";
import { Popover, Transition } from "@headlessui/react";
import Link from "next/link";

export default function Header() {
  const [navbar, setNavbar] = useState(false);

  const changeNavbar = () => {
    if (window.scrollY >= 80) {
      setNavbar(true);
    } else {
      setNavbar(false);
    }
  };

  React.useEffect(() => {
    window.addEventListener("scroll", changeNavbar);
  }, []);

  return (
    <Wrapper>
      <div>
        <Popover>
          {({ open }) => (
            <>
              <div>
                <div>
                  <div>
                    <span>Name</span>
                    <Link href="/">
                      <img
                        src="/images/logos/logo_blue.png"
                        alt=""
                      />
                    </Link>
                  </div>
                  <div>
                    <Popover.Button>
                      <span>Open menu</span>
                      <MenuIcon aria-hidden="true" />
                    </Popover.Button>
                  </div>
                  <Popover.Group as="nav">
                    {main.map((item) => (
                      <Link href={item.href} key={item.name}>
                        <a key={item.name}>
                          {item.name}
                        </a>
                      </Link>
                    ))}

                    <Popover>
                      {({ open }) => (
                        <>
                          <Popover.Button>
                            <span>More</span>
                            <ChevronDownIcon
                              aria-hidden="true"
                            />
                          </Popover.Button>

                          <Transition
                            show={open}
                            as={Fragment}
                            enter="transition ease-out duration-200"
                            enterFrom="opacity-0 translate-y-1"
                            enterTo="opacity-100 translate-y-0"
                            leave="transition ease-in duration-150"
                            leaveFrom="opacity-100 translate-y-0"
                            leaveTo="opacity-0 translate-y-1"
                          >
                            <Popover.Panel
                              static>
                              <div>
                                <div>
                                  {resources.map((item) => (
                                    <a
                                      key={item.name}
                                      href={item.href}
                                      >
                                      <item.icon
                                        aria-hidden="true"
                                      />
                                      <div>
                                        <p>
                                          {item.name}
                                        </p>
                                        <p>
                                          {item.description}
                                        </p>
                                      </div>
                                    </a>
                                  ))}
                                </div>
                               </div>
                            </Popover.Panel>
                          </Transition>
                        </>
                      )}
                    </Popover>
                  </Popover.Group>
                  <div>
                    <a
                      href="/login">
                      Sign in
                    </a>
                    <a
                      href="/login"
                    >
                      Sign up
                    </a>
                  </div>
                </div>
              </div>

              <Transition
                show={open}
                as={Fragment}
                enter="duration-200 ease-out"
                enterFrom="opacity-0 scale-95"
                enterTo="opacity-100 scale-100"
                leave="duration-100 ease-in"
                leaveFrom="opacity-100 scale-100"
                leaveTo="opacity-0 scale-95"
              >
                <Popover.Panel
                  focus
                  static
                 >
                  <div>
                    <div>
                      <div>
                        <Link href="/">
                          <img
                            src="/images/logos/logo_blue.png"
                            alt="Workflow"
                          />
                        </Link>
                        <div className="-mr-2">
                          <Popover.Button>
                            <span >Close menu</span>
                            <XIcon aria-hidden="true" />
                          </Popover.Button>
                        </div>
                      </div>
                      <div>
                        <nav>
                          {main.map((item) => (
                            <a
                              key={item.name}
                              href={item.href}
                            >
                              <item.icon
                                aria-hidden="true"
                              />
                              <span>
                                {item.name}
                              </span>
                            </a>
                          ))}
                        </nav>
                      </div>
                    </div>
                    <div>
                      <div>
                        {resources.map((item) => (
                          <a
                            key={item.name}
                            href={item.href}
                          >
                            {item.name}
                          </a>
                        ))}
                      </div>
                      <div>
                        <a
                          href="/login"
                        >
                          Sign up
                        </a>

                        <p>
                          Existing customer?{" "}
                          <a href="/login">
                            Sign in
                          </a>
                        </p>
                      </div>
                    </div>
                  </div>
                </Popover.Panel>
              </Transition>
            </>
          )}
        </Popover>
      </div>
    </Wrapper>
  );
}

like image 975
Mathias Riis Sorensen Avatar asked Oct 14 '25 14:10

Mathias Riis Sorensen


1 Answers

I am using TailwindCSS and HeadlessUI on a ReactJS project for my school and I was wondering the same thing.

My solution was to wrap the popover panel items in a Popover.Button and invoke a function that set the Popover's own 'open' to false and as a result the menu will close.

I'm taking only 1 example from your code:

{main.map((item) => (

    <Popover.Button onClick={() => (open = false)}>

       <Link href={item.href} key={item.name}>
         <a key={item.name}>
           {item.name}
         </a>
       </Link>

     <Popover.Button>

I hope this helps solve your case as well as it did for me :)

like image 174
Loren K. Avatar answered Oct 18 '25 03:10

Loren K.