Ability to unmount Menu.Item
See original GitHub issueFrom the docs I’m not sure what the unmount
parameter is supposed to do, so I can’t say for sure this is a bug, but here goes:
Going by this example: https://github.com/tailwindlabs/headlessui/blob/develop/packages/%40headlessui-react/pages/menu/menu-with-framer-motion.tsx, you need a render prop to get framer-motion exit
animations to work:
<Menu>
{({ open }) => (
<>
<Menu.Button>...</Menu.Button>
<AnimatePresence>
{open && (
<Menu.Items
static
as={motion.div}
initial={{ opacity: 0, y: 0 }}
animate={{ opacity: 1, y: '0.5rem' }}
exit={{ opacity: 0, y: 0 }}
>
...
</Menu.Items>
)}
</AnimatePresence>
</>
)}
</Menu>
However, since framer-motion does its own mount/unmount detection, we actually don’t need this, and could (in theory) just do this:
<Menu>
<Menu.Button>...</Menu.Button>
<AnimatePresence>
<Menu.Items
unmount
as={motion.div}
initial={{ opacity: 0, y: 0 }}
animate={{ opacity: 1, y: '0.5rem' }}
exit={{ opacity: 0, y: 0 }}
>
...
</Menu.Items>
</AnimatePresence>
</Menu>
Which is of course a lot simpler and looks better in my opinion.
However, this doesn’t work as the <Menu.Items>
element is never unmounted from the tree, even with the unmount
parameter:
This blocks <AnimatePresence>
from briefly keeping the element in DOM and firing the exit animation.
I would expect the <Menu.Items>
to disappear from the React DOM, especially when the unmount
parameter is set. It’s unclear to me why it hangs around, especially as the linked example removes it from the DOM as well (with the {open && (
), and it seems to work just fine. Also, the behaviour doesn’t seem to change when I switch to unmount={false}
. So that’s why I reason it’s probably a bug.
Issue Analytics
- State:
- Created 3 years ago
- Comments:11
Hey! Thank you for your bug report! Much appreciated! 🙏
The
unmount
prop feature is not yet released, butunmount={true}
is the default behaviour. The idea is that when you addunmount={true}
you will unmount the DOM nodes when theMenu.Items
should be invisible. When you setunmount={false}
then the DOM nodes will still be they will be marked with ahidden
attribute and astyle={{ display: 'none' }}
That said, the
unmount
strategy is used in a DOM perspective. So the actual React node will still be there, as far as I know you can’t unmount yourself. Reason being is that once you provide a prop to theMenu.Items
component, the logic happening is already inside the component, so you are already too late if that makes sense. So instead we hide/unmount the children of theMenu.Items
component.I know you can do some magic where you tell your parent that you don’t want to be rendered anymore I think. But since we are not always a direct child of the parent then this can get hairy.
I’m not sure if this answers your question.
Hey! Sorry that I didn’t respond to the issue in a while.
Indeed, there is no other (good) solution to this as far as I know. Another solution I know of is looping through all the children recursively and removing them but I don’t think that’s a good solution.
I’m afraid that you will need to keep using the solution with the more verbose syntax you initially used.
If other issues are present, feel free to open a new issue!