Jul 5, 2025

Clean & Easy Framer Motion Wrapper for Next.js & React

Clean & Easy Framer Motion Wrapper for Next.js & React

Learn how a simple MotionWrapper component can bring reusable, clean, and scalable animations to your Next.js project.

  • Jorge Perez Avatar
    Jorge Perez
    4 min read
  • Motion Journey

    I recently decided to dive into animation and bring some motion into my developer portfolio. I was especially excited to use Framer Motion for smooth page and component transitions in my Next.js 14/15 projects.

    But as my pages multiplied and my codebase grew, I started seeing the same animated blocks over and over:

    import { motion } from "framer-motion";
    
    <motion.div initial={...} animate={...} transition={...}>
      {/* cool stuff */}
    </motion.div>

    I began thinking, there must be a cleaner, reusable pattern, especially for larger apps and teams. So, I set off on a weekend of experimenting, refactoring, and (mis)reading docs. Here’s what I discovered, and why I landed on the MotionWrapper.tsx approach below.

    The Problem: Repetition, Rigidity, and Scalability

    Framer Motion’s API is direct:

    • Import motion
    • Pick your tag (motion.div)
    • Pass your animation props

    For quick demos, it’s great. But on a big Next.js site, you start to want:

    • DRY code: Avoid rewriting the same initial, animate, and transition blocks everywhere.
    • Centralized changes: Animation defaults in one place.
    • Easy migration: What if we ever want to swap out Framer Motion, or add metrics/analytics?
    • Team conventions: Make it obvious how to animate anything, without copy/pasting docs into every PR.

    My Experiment: Wrapping motion.div

    After poking around the official docs and a handful of GitHub threads, I found little on "wrapping motion components." But I tried it anyway.

    Step one was a TypeScript-first wrapper:

    // components/MotionWrapper.tsx
    
    'use client';
    
    import { motion, ComponentPropsWithoutRef } from 'framer-motion';
    
    /**
     * A reusable, type-safe wrapper for motion.div.
     */
    export type MotionDivProps = ComponentPropsWithoutRef<typeof motion.div>;
    
    export const MotionDiv = (props: MotionDivProps) => <motion.div {...props} />;
    

    Why this approach?

    • It forwards all props, including initial, animate, transition, and any HTML div attributes.
    • The types always match whatever changes in framer-motion’s internals.
    • It gives you a single place to set defaults, instrument analytics, or swap animation libraries.

    Putting It Into Practice: Animated Profile Card

    This component card is from my NeatDev Template. With the MotionWrapper animating any component is as simple as:

    import { MotionDiv } from '@/components/MotionWrapper';
    
    export function ProfileCard() {
      return (
        <MotionDiv
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.5, ease: 'easeOut' }}
          className="your-styles"
        >
          {/* Your entire component/section/etc. */}
        </MotionDiv>
      );
    }

    And, for example, your entire page:

    export default function HomePage() {
      return (
        <MotionDiv initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
          <h1>Hello, Next.js + Framer Motion!</h1>
          {/* ... */}
        </MotionDiv>
      );
    }

    This means every developer now knows exactly where to look for animation conventions, and any project-wide change is a one-liner.

    Best Practices I Landed On

    Through trial and error, here are my takeaways:

    • Only Wrap When You Need To: If you’re building a tiny site, direct motion.div is fine. Wrapping pays off as code grows or teams scale.
    • Type Carefully: Use ComponentPropsWithoutRef<typeof motion.div> to keep types as tight as possible (for future-proofing).
    • Centralize Defaults Sparingly: If you want all motion divs to share an animation (e.g., a fade-in), set it in MotionDiv. Otherwise, pass props per usage.
    • Document the Pattern: Leave a comment in the component about why you’re wrapping, so new devs understand when to use it.
    • Reference the Docs: Stay up-to-date, the Framer Motion’s API evolves, and keeping your wrapper simple makes maintenance easy. Official docs: Framer Motion React Quick Start.

    Why I Use This in Production

    After several weeks, this pattern has made animations more consistent, easier to refactor, and more approachable. It also opened the door to other wrappers (like for transitions, modals, or even “motion.section” elements).

    Animation should be fun and simple, not another source of tech debt. A single, dedicated MotionWrapper lets you focus on the fun part: creating vibrant interfaces.

    Final Thoughts: When to [Not] Use This

    This isn’t a silver bullet. If you only animate one or two things, the wrapper may add unnecessary indirection. But for modern Next.js, TypeScript-heavy, or team-based projects, I find this consistently leads to cleaner, more maintainable code.

    If nothing else, experimenting with your own MotionWrapper will teach you a lot about TypeScript, React props, and scalable front-end patterns. Try it, test it, and build something that’s not just animated, but maintainable, too.

    You Might Also Like...