Combining Menu and Framer Motion to create a dropdown menu with spring animation and staggered children.
In the example below, we're creating a reusable Menu component (menu.tsx) that accepts Framer Motion's props.
Menu
menu.tsx
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849import { useState } from "react"; import { MotionProps, useReducedMotion } from "framer-motion"; import { Menu, MenuItem, MenuSeparator } from "./menu"; import "./style.css"; export default function Example() { const [open, setOpen] = useState(false); const reducedMotion = useReducedMotion(); const itemProps: MotionProps = reducedMotion ? {} : { transition: { opacity: { duration: 0.2 } }, variants: { closed: { opacity: 0, x: -16 }, open: { opacity: 1, x: 0 }, }, }; return ( <Menu label="Options" open={open} setOpen={setOpen} animate={open ? "open" : "closed"} variants={{ closed: reducedMotion ? { opacity: 0 } : { scale: 0 }, open: reducedMotion ? { opacity: 1 } : { scale: 1, transition: { type: "spring", duration: 0.4, delayChildren: 0.2, staggerChildren: 0.05, }, }, }} > <MenuItem {...itemProps}>Edit</MenuItem> <MenuItem {...itemProps}>Share</MenuItem> <MenuItem {...itemProps}>Delete</MenuItem> <MenuSeparator /> <MenuItem {...itemProps}>Report</MenuItem> </Menu> ); }
import { useState } from "react"; import { MotionProps, useReducedMotion } from "framer-motion"; import { Menu, MenuItem, MenuSeparator } from "./menu"; import "./style.css"; export default function Example() { const [open, setOpen] = useState(false); const reducedMotion = useReducedMotion(); const itemProps: MotionProps = reducedMotion ? {} : { transition: { opacity: { duration: 0.2 } }, variants: { closed: { opacity: 0, x: -16 }, open: { opacity: 1, x: 0 }, }, }; return ( <Menu label="Options" open={open} setOpen={setOpen} animate={open ? "open" : "closed"} variants={{ closed: reducedMotion ? { opacity: 0 } : { scale: 0 }, open: reducedMotion ? { opacity: 1 } : { scale: 1, transition: { type: "spring", duration: 0.4, delayChildren: 0.2, staggerChildren: 0.05, }, }, }} > <MenuItem {...itemProps}>Edit</MenuItem> <MenuItem {...itemProps}>Share</MenuItem> <MenuItem {...itemProps}>Delete</MenuItem> <MenuSeparator /> <MenuItem {...itemProps}>Report</MenuItem> </Menu> ); }