In Next.js App Router, pages are Server Components by default.
This means that if you want to add animation to your components using Framer Motion, you can't directly use the motion
component provided by Framer Motion.
This is because the motion
component needs access to DOM nodes to perform animations; however, Server Components in Next.js run on the server and don't have access to the DOM.
Let's explore how to solve this problem.
The Problem
Let’s say you have a component like this:
This is a Server Component. When this component renders, you want to give the h1
element an enter animation: fade in and slide up from the bottom. However, because <HomePage>
is a Server Component, you can't directly use the corresponding motion
component for the h1
element, like this:
You'll get an error:
The Solution
To solve this problem, you need to create a custom component that wraps the motion
component from Framer Motion. This custom component will be a Client Component, which you can use inside Server Components.
Here's how you can create a custom component:
Let's break down what's happening in this code:
- We create a component called
<MotionH1>
usingReact.forwardRef
. - Inside
forwardRef
, we have a render function called<MotionH1>
that takes two parameters:props
andref
. We destructure theprops
parameter to extractchildren
, and any remaining props using the spread operator...props
. We then pass these props, along with theref
, to the<motion.h1>
component. - Finally, we export
<MotionH1>
to be used inside Server Components.
Now that you have an overall understanding of what the code does, let's understand more about forwardRef
.
Understanding forwardRef
forwardRef
is an API in React that allows a component to expose a DOM node to its parent component with a ref
.
In our case, <MotionH1>
uses forwardRef
to obtain the ref
passed to it by Framer Motion and forward that ref
to the underlying <motion.h1>
component. This lets Framer Motion access the h1
DOM node exposed by <MotionH1>
Now that you understand the what and why behind forwardRef
, let's understand how to type it.
Typing forwardRef
The syntax for forwardRef
with TypeScript looks like this:
To type forwardRef
, you need two type parameters:
RefType
: Represents the type of the DOM element that theref
will be attached to. In our case, it'sHTMLHeadingElement
because we're rendering anh1
element.PropsType
: Represents the props that the component will receive. We define this asMotionH1Props
, which is an alias forHTMLMotionProps<'h1'>
.
HTMLMotionProps
is a generic type provided by Framer Motion that represents the props for a motion component based on a specific HTML element. By passing h1
to HTMLMotionProps
, we're telling it to include all the valid props for an h1
element along with motion-specific props (such as initial
, animate
, and exit
) from Framer Motion.
Now, you can import and use the <MotionH1>
component inside the Server Component:
When <HomePage>
renders, you'll see the enter animation on the h1
element.
But an h1
is not the only HTML element you'll want to animate. You might also want to animate a div
, a section
, a p
element, and more. In such cases, you'll have to create wrapper components for each element you want to animate, such as MotionDiv
, MotionSection
, MotionP
, etc.
To keep your code organized, you can create a single file called use-client.tsx
in your components
folder. Inside this file, you can define all your motion component wrappers and export them.