Building a Reusable MotionDiv Component for Next.js Applications

Learn how to create a flexible MotionDiv component that works seamlessly with Next.js server-side rendering while maintaining clean and reusable animation code.

Sushil Magare /
Building a Reusable MotionDiv Component for Next.js Applications

When working with animations in Next.js, you'll quickly realize that Framer Motion components can't be used directly in server-side rendered pages. This is where a reusable MotionDiv component becomes incredibly valuable. Let's explore why you need it and how to build one that works perfectly with Next.js.

The Problem with Framer Motion and SSR

Next.js renders pages on the server by default, but Framer Motion relies on browser APIs that don't exist on the server. This creates a mismatch between server and client rendering, leading to hydration errors.

1// ❌ This will cause hydration errors in Next.js
2import { motion } from "framer-motion";
3
4export default function HomePage() {
5return (
6  <motion.div
7    initial={{ opacity: 0 }}
8    animate={{ opacity: 1 }}
9  >
10    Server-side rendered content
11  </motion.div>
12);
13}

The Solution: A Reusable MotionDiv Component

Here's how we can create a MotionDiv component that solves these issues:

1"use client";
2
3import { motion, HTMLMotionProps } from "motion/react";
4import { cn } from "@/lib/utils";
5
6interface MotionDivProps extends HTMLMotionProps<"div"> {
7className?: string;
8}
9
10const MotionDiv = ({ children, className, ...motionProps }: MotionDivProps) => {
11return (
12
13<motion.div className={cn(className)} {...motionProps}>
14{children}
15</motion.div>
16); };
17
18export default MotionDiv;

Why This Component is So Useful

Let's break down the key benefits of this approach:

Client-Side Only

The "use client" directive ensures this component only runs on the client side, preventing SSR conflicts while keeping your animations smooth.

Full Framer Motion API

By extending HTMLMotionProps<"div">, we get all the motion properties like initial, animate, whileHover, and more with full TypeScript support.

Flexible Styling

The cn() utility (usually from clsx or similar) allows for easy className composition, making it work perfectly with Tailwind CSS and other styling solutions.

Drop-in Replacement

It works exactly like a regular motion.div, so you can use it anywhere without changing your existing animation logic.

How to Use Your MotionDiv Component

Now you can use animations throughout your Next.js app without worrying about SSR issues:


import MotionDiv from "@/components/MotionDiv";

export default function AnimatedCard() {
return (
  <MotionDiv
    initial={{ opacity: 0, y: 20 }}
    animate={{ opacity: 1, y: 0 }}
    transition={{ duration: 0.5 }}
    className="rounded-lg bg-white p-6 shadow-lg"
  >
    <h2>Animated Content</h2>
    <p>This works perfectly with Next.js SSR!</p>
  </MotionDiv>
);
}

When to Use MotionDiv vs Regular Motion Components

Here's a simple guide for when to use each approach:

1// ✅ Use MotionDiv in Next.js pages and server components
2import MotionDiv from "@/components/MotionDiv";
3
4export default function ServerRenderedPage() {
5return (
6  <div>
7    <h1>Server-rendered content</h1>
8    <MotionDiv initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
9      This animates safely!
10    </MotionDiv>
11  </div>
12);
13}
14
15// ✅ Use regular motion components in client-only contexts
16"use client";
17import { motion } from "framer-motion";
18
19export default function ClientOnlyComponent() {
20return (
21  <motion.div animate={{ rotate: 360 }}>
22    This is already client-side only
23  </motion.div>
24);
25}

Making It Even Better

You can extend this pattern to create other reusable motion components:

1// MotionButton.tsx
2"use client";
3import { motion, HTMLMotionProps } from "motion/react";
4import { cn } from "@/lib/utils";
5
6interface MotionButtonProps extends HTMLMotionProps<"button"> {
7variant?: "primary" | "secondary";
8}
9
10const MotionButton = ({
11children,
12className,
13variant = "primary",
14...motionProps
15}: MotionButtonProps) => {
16const baseStyles = "px-4 py-2 rounded-md font-medium";
17const variants = {
18primary: "bg-blue-600 text-white hover:bg-blue-700",
19secondary: "bg-gray-200 text-gray-800 hover:bg-gray-300"
20};
21
22return (
23
24<motion.button
25className={cn(baseStyles, variants[variant], className)}
26whileHover={{ scale: 1.02 }}
27whileTap={{ scale: 0.98 }}
28{...motionProps}
29>
30{children}
31</motion.button>
32); };
33
34export default MotionButton;

Conclusion

Creating reusable motion components like MotionDiv is essential for building smooth, animated Next.js applications. By wrapping Framer Motion components with the "use client" directive and proper TypeScript interfaces, you get the best of both worlds: server-side rendering performance and beautiful client-side animations.

This pattern keeps your code clean, prevents hydration errors, and makes animations feel like a natural part of your Next.js development workflow. Start building your own motion component library today!