Replies: 4 comments
-
Same issue with Popover. Unable to handle exit animations |
Beta Was this translation helpful? Give feedback.
-
I actually found a solution that worked recently, forgot I had posted about this! Here's how I solved to get framer motion enter/exit animations working with Headless UI's const Tabs = () => {
const [tabIndex, setTabIndex] = React.useState(0)
return (
<Tab.Group
selectedIndex={tabIndex}
onChange={(index) => setTabIndex(index)}
manual
>
<Tab.List>
{tabs.map((tab, key) => (
<Tab key={key} as={React.Fragment}>
{/* Tab button here... */}
</Tab>
))}
</Tab.List>
<Tab.Panels>
<AnimatePresence exitBeforeEnter>
<Tab.Panel
key={tabIndex}
as={m.div}
initial="hide"
animate="show"
exit="hide"
variants={tabAnim}
static
>
{/* Selected tab content here... */}
</Tab.Panel>
</AnimatePresence>
</Tab.Panels>
</Tab.Group>
)
} Here's the framer motion variant for context: const tabAnim = {
hide: {
opacity: 0,
transition: {
duration: 0.2,
ease: 'linear',
when: 'beforeChildren',
},
},
show: {
opacity: 1,
transition: {
duration: 0.2,
ease: 'linear',
when: 'beforeChildren',
},
},
} |
Beta Was this translation helpful? Give feedback.
-
Set |
Beta Was this translation helpful? Give feedback.
-
Here is a solution for the latest version of headless ui and framer motion: import { Tab } from "@headlessui/react";
import { AnimatePresence, motion, type Variants } from "framer-motion";
import { useState } from "react";
export default function PanelContent() {
const [selectedIndex, setSelectedIndex] = useState(0);
return (
<Tab.Group selectedIndex={selectedIndex} onChange={setSelectedIndex}>
<Tab.List>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
</Tab.List>
<Tab.Panels>
<AnimatePresence
mode="wait" // let the exit animation finish before removing the element
initial={false} // avoid animating on first render (optional)
>
{selectedIndex === 0 && (
<MotionTabPanel key={selectedIndex}>Tab 1 Content</MotionTabPanel>
)}
{selectedIndex === 1 && (
<MotionTabPanel key={selectedIndex}>Tab 2 Content</MotionTabPanel>
)}
</AnimatePresence>
</Tab.Panels>
</Tab.Group>
);
}
const variants: Variants = {
visible: {
opacity: 1,
transition: { duration: 0.3, delay: 0.5, ease: "easeOut" },
},
hidden: {
opacity: 0,
transition: {
duration: 0.3,
ease: "easeOut",
},
},
};
function MotionTabPanel({ children }: { children: React.ReactNode }) {
return (
<Tab.Panel
as={motion.div}
variants={variants}
initial="hidden"
animate="visible"
exit="hidden"
static // tells headless ui to delegate mount/unmount to us
>
{children}
</Tab.Panel>
);
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Hey there! Loving Headless UI so far 🤘
I'm trying to use framer-motion with
Tab.Panels
, but it seems that the way headlessUI handles updating the activeTab.Panel
does not play nice with framer-motion'sAnimatePresence
and exit animations.Normally, you can add a
key
prop tomotion
components and if wrapped inAnimatePresence
(withexitBeforeEnter
) you can trigger exit animations when the component is removed from the dom.This approach does not seem to work with headlessUI Tabs. For example, consider the following:
But when changing tabs the outgoing
Tab.Panel
disappears abruptly, while the newly selectedTab.Panel
animates in properly.I've tried several other combinations, like:
Tab.Panel
as aFragment
and setting up the motion component within thatAnimatePresence
outside of theTab.Panels
wrapper componentselectedIndex
renderProp to show/hide the motion divmanual
toTab.Group
and utilizingonChange
to control the selected indexNothing has worked! The closest I was able to get caused the outgoing motion div (
Tab.Panel
) to abruptly hydrate with the newly selected tab data while it was animating out.Has anyone successfully been able to handle enter/exit transitions with Tabs? Really feel like this is a huge limitation here, in an otherwise lovely library!
Beta Was this translation helpful? Give feedback.
All reactions