Search code examples
reactjsreact-routertailwind-cssframer-motionborder-radius

How to properly hook up framer-motion and React Router Link?


I'm integrating animation into my links with framer-motion. Currently, I'm using React Router links and styling them accordingly, followed by wrapping them with motion.div for animation effects. However, I've encountered an issue: when the links possess border-radius and are enveloped within motion.div, the animation triggers prematurely, before entering the circular link boundary.

Edit framer-motion-react-router-tailwind

While I understand that I can adjust styles by applying them to motion.div directly, I prefer to maintain consistency by styling only one element. My goal is to determine whether it's feasible to render motion.div as a Router Link, or vice versa. How can I approach resolving this challenge?

Additionally, in my actual project, I rely on Tailwind CSS. When attempting to animate background-color changes on motion.div, I can't access to Tailwind's color palette. How would you suggest addressing this constraint?


Solution

  • I opened your CodeSandbox and I see your issue is happening because the mouse encounters the motion.div element area (invisible rectangle around the circle).

    At first, I added a width: fit-content to motion.div to minimize the wrapper component width and achieve your visual goal. But there is a big problem here, this approach needs to customize motion.div style as well as the children.

    But I started to read the official Framer Motion docs and I found this feature: https://www.framer.com/motion/component/#custom-components

    As I understand your issue, you can create a custom wrapper component like:

    const MotionComponent = ({ as, children, ...props }) => {
      const ChildrenComponent = motion(as, {
        forwardMotionProps: true,
      });
    
      return <ChildrenComponent {...props}>{children}</ChildrenComponent>;
    };
    

    and use it like:

    <MotionComponent
      as={Link}
      to="/"
      className="link"
      whileHover={{ scale: 1.1, transition: { duration: 0.25 } }}
    >
      Home
    </MotionComponent>