Search code examples
typescriptnext.jstypestypeerrorframer-motion

How can I use Nextjs Link Component with framer motion in Typescript?


Description

I want to animate a nextjs Link component using framer motion. I'm also using TypeScript and It keeps giving me a type error of:

Property 'Link' does not exist on type '(<Props extends {}>(Component: string | ComponentType<PropsWithChildren<Props>>, customMotionComponentConfig?: CustomMotionComponentConfig | undefined) => CustomDomComponent<...>) & HTMLMotionComponents & SVGMotionComponents'. Did you mean 'link'?

Code

Here's the component I'm making.

export default function Button({ href, text, type, ...props }: ButtonTypes) {
  console.log(styles);
  if (href) {
    return (
      <motion.Link
        href={href}
        type={type}
        className={`${styles['neon-button']} focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-aquamarine`}
        {...props}
      >
        {text}
      </motion.Link>
    );
  }
  return (
    <motion.button
      whileHover={{ scale: 1.1 }}
      whileTap={{ scale: 0.9 }}
      type={type}
      className="neon-button focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-aquamarine"
      {...props}
    >
      {text}
    </motion.button>
  );
}

Expectations

The component animates when I add the whileHover and whileTap properties, but obviously I need to get rid of the type error.


Solution

  • You can do so by wrapping Component in motion() function.

    const Component = React.forwardRef((props, ref) => (
        <div ref={ref} />
    ))
    
    const MotionComponent = motion(Component)
    
    const MotionLink= motion(Link)
    

    Framer Custom Component

    If you don't like this there are some workarounds as well:

    Workaround 1: One possible work around is to wrap your next Link in a div and use framer motion on wrapper div.

    <motion.div
       className={styles.linkWrapper}
       //your framer motion props goes here
    >
      <Link href="#"> Lorem </Link>
    </motion.div>
    

    --

    Workaround 2: Use <motion.a> tag instead of <Link> and use useRouter to implement functionality of Link on a tag.

    Example:

    const router = useRouter()
    
     <motion.a
        href="/contact"
        onClick={(e) => {
           e.preventDefault();
           router.push("/contact");
        }}
        className={styles.navLink}
     >
        Lorem
    </motion.a>
    

    Learn more at: Next useRouter Hook official documentation