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:
export default function HomePage() {
return <h1 className="text-2xl font-bold">Welcome Home!</h1>
}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:
import { motion } from "framer-motion"
export default function HomePage() {
return (
<motion.h1
className="text-2xl font-bold"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
>
Welcome Home!
</motion.h1>
)
}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:
"use client"
import React from "react"
import { motion } from "framer-motion"
import type { HTMLMotionProps } from "framer-motion"
type MotionH1Props = HTMLMotionProps<"h1">
const MotionH1 = React.forwardRef<HTMLHeadingElement, MotionH1Props>(
function MotionH1({ children, ...props }, ref) {
return (
<motion.h1 ref={ref} {...props}>
{children}
</motion.h1>
)
}
)
export { MotionH1 }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:propsandref. We destructure thepropsparameter 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:
const Component = React.forwardRef<RefType, PropsType>((props, ref) => {
// Component implementation
})To type forwardRef, you need two type parameters:
RefType: Represents the type of the DOM element that therefwill be attached to. In our case, it'sHTMLHeadingElementbecause we're rendering anh1element.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:
import { MotionH1 } from "@/components/use-client"
export default function HomePage() {
return (
<MotionH1
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
className="text-2xl font-bold"
>
Welcome Home!
</MotionH1>
)
}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.
