DEV Community

Sushil
Sushil

Posted on

Animate Like a Pro: Creating a Reusable MotionDiv Using Motion

If you're working with animations in Next.js, you've probably found yourself writing the same Motion code over and over again. Let's solve that by creating a simple, reusable MotionDiv component that will save you time and keep your animations consistent.

What We're Building

We're going to create a wrapper component around Motion's motion.div that provides better TypeScript support and integrates seamlessly with our existing utilities. Here's the complete component:

'use client';

import { motion, HTMLMotionProps } from 'framer-motion';
import { cn } from '@/lib/utils';

interface MotionDivProps extends HTMLMotionProps<'div'> {
    className?: string;
}

const MotionDiv = ({ children, className, ...motionProps }: MotionDivProps) => {
    return (
        <motion.div className={cn(className)} {...motionProps}>
            {children}
        </motion.div>
    );
};

export default MotionDiv;
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

1. Install Dependencies

First, make sure you have Motion installed in your Next.js project:

npm install motion
Enter fullscreen mode Exit fullscreen mode

2. Create the Component

Create a new file components/ui/MotionDiv.tsx (or wherever you keep your components) and add the code above.

3. The 'use client' Directive

Since we're using Next.js with the App Router, we need the' use client' directive. This tells Next.js that this component should run on the client side.

4. TypeScript Interface

The MotionDivProps interface extends HTMLMotionProps<'div'>, which means our component gets all the props that motion.div accepts — including standard div props like className, animation props, and event handlers.

How to Use It

Once you've created the component, using it is straightforward:

Basic Fade-in Animation

<MotionDiv
    initial={{ opacity: 0 }}
    animate={{ opacity: 1 }}
    transition={{ duration: 0.5 }}
    className="p-4 bg-white rounded-lg shadow-lg"
>
    <h2>This content fades in</h2>
    <p>Much cleaner than writing motion.div everywhere!</p>
</MotionDiv>
Enter fullscreen mode Exit fullscreen mode

Slide Animation

<MotionDiv
    initial={{ x: -50, opacity: 0 }}
    animate={{ x: 0, opacity: 1 }}
    className="notification-banner"
>
    New notification!
</MotionDiv>
Enter fullscreen mode Exit fullscreen mode

Hover Effects

<MotionDiv
    whileHover={{ scale: 1.02 }}
    whileTap={{ scale: 0.98 }}
    className="card cursor-pointer"
>
    Interactive card content
</MotionDiv>
Enter fullscreen mode Exit fullscreen mode

Why Make It Reusable?

1. Less Repetitive Code

Instead of importing motion and writing motion.div throughout your app, you just import and use MotionDiv. It might seem small, but it adds up across a large project.

2. Consistent Animations

When your team uses the same component, you naturally end up with more consistent animation patterns. No more wondering why some elements animate differently than others.

3. Better TypeScript Experience

You get full TypeScript support with autocompletion for all Framer Motion props. If you make a typo in an animation property, TypeScript will catch it.

4. Easy to Extend

Need to add default animations or custom presets later? You can modify this one component instead of hunting down every motion.div in your codebase.

5. Utility Integration

The cn function (usually from libraries like clsx or custom utility functions) handles className merging and conditional classes. This works great with Tailwind CSS or any CSS framework.

Real-World Example

Here's how you might use it in a typical Next.js 15 page:

// app/page.tsx
import MotionDiv from '@/components/ui/MotionDiv';

export default function HomePage() {
    return (
        <main className="container mx-auto px-4">
            <MotionDiv
                initial={{ y: 20, opacity: 0 }}
                animate={{ y: 0, opacity: 1 }}
                transition={{ duration: 0.6 }}
                className="text-center py-16"
            >
                <h1 className="text-4xl font-bold mb-4">Welcome to Our Site</h1>
                <p className="text-lg text-gray-600">
                    This hero section animates in smoothly
                </p>
            </MotionDiv>

            <MotionDiv
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                transition={{ delay: 0.3, duration: 0.6 }}
                className="grid grid-cols-1 md:grid-cols-3 gap-6 py-12"
            >
                {/* Your content here */}
            </MotionDiv>
        </main>
    );
}
Enter fullscreen mode Exit fullscreen mode

Taking It Further

Once you're comfortable with the basic MotionDiv, you can extend it with features like:

  • Animation presets: Add common animation patterns as props
  • Default values: Set up sensible defaults for common use cases
  • Conditional animations: Toggle animations based on user preferences
  • Stagger support: Built-in support for staggered animations

Wrapping Up

Creating a reusable MotionDiv component is a simple way to make your Next.js animations more maintainable and consistent. It takes just a few minutes to set up, but you'll appreciate the cleaner code and better developer experience every time you add animations to your app.

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.