Native popovers in React

11 March 2026

The Popover API has been part of all major browsers since early 2024.

It lets you turn any HTML element into a popover by just adding a popover attribute to it. You can then toggle the visibility using JavaScript, with the showPopover and hidePopover methods, or using only HTML by using popovertarget as in the example below:

<button popovertarget="example">Toggle popover</button>
<div id="example" popover>I am a popover!</div>

What a killer set of features! I recommend reading the MDN documentation to learn more.

Unfortunately, not everybody has the luxury of writing HTML at their job, so I ended up writing a small hook that makes it easier to use native popovers in React-land.

The example assumes you are using React 19. In prior versions, the onToggle and onBeforeToggle events wouldn’t work and you had to manually add an event listener for toggle and beforetoggle. Also, you had to memoize the callbacks.

Here is the hook:

import React from "react";
import { useState, useRef } from "react";

function usePopover({
  behavior = "auto",
  onToggle,
  onBeforeToggle,
} = {}) {
  const ref = useRef(null);
  const [isOpen, setIsOpen] = useState(false);

  function handleToggleEvent(event) {
    setIsOpen(event.newState === "open");
    onToggle?.(event);
  }

  function showPopover() {
    ref.current?.showPopover?.();
  }

  function hidePopover() {
    ref.current?.hidePopover?.();
  }

  function togglePopover() {
    ref.current?.matches(":popover-open")
      ? hidePopover()
      : showPopover();
  }

  return {
    isOpen,
    showPopover,
    hidePopover,
    togglePopover,
    popoverProps: {
      ref,
      popover: behavior,
      onToggle: handleToggleEvent,
      onBeforeToggle,
    },
  };
}

And this is how you use it:

function Component() {
  const { popoverProps, showPopover, isOpen } = usePopover({
    onToggle: () => console.log("toggled")
  });

  return (
    <>
      <button onClick={showPopover}>Open popover</button>
      <p>{isOpen ? "Open" : "Closed"}</p>
      <div {...popoverProps}>I am popover</div>
    </>
  );
}

I don’t know about you, but I really like these small utility hooks. This one, in particular, I find is a great foundation to build more complex headless components, check it out!