• Homeright arrow
  • Blogright arrow
  • How to Create Reusable Animation Components in Next.js Using Motion Variants
Feature

How to Create Reusable Animation Components in Next.js Using Motion Variants

Motion is one of the most powerful animation libraries in the React ecosystem. With the latest update, the library has moved beyond Framer Motion’s original API and introduced a cleaner, more flexible approach to animations—including new ways to work with Variants, making it easier than ever to create reusable animation components.

In this blog post, we'll walk through how to build and reuse animation components using the latest motion package in a Next.js project. We'll explore how variants help us manage animation states in a clean, maintainable way

What Are Motion Variants?

Variants are configuration objects that define different animation states for your component. You declare the possible states (initial, animate, exit, etc.) and apply them declaratively via props. This makes them perfect for reuse across components.

Getting Started

First, install the Motion package:

npm install motion
// or
yarn add motion

Create a basic Next.js component using motion.div from the latest Motion API. We'll define a simple fade-in animation with a subtle upward slide when the component appears and a fade-out with a downward slide when it's removed. The animation will be controlled through a variant with three states:

initial: The element starts fully transparent (opacity: 0) and positioned slightly lower (y: -20).

animate: It moves to its natural position (y: 0) and becomes fully visible (opacity: 1) over 0.6 seconds.

exit: When removed, it fades out and slides down (y: -20) again.

We'll apply this variant to a motion.div inside a reusable FadeIn component, allowing you to wrap and animate content anywhere in your application easily.

// components/FadeIn.tsx
'use client'
import { motion } from “motion/react”

const fadeInVariant = {
initial: { opacity: 0, y: -20 },
animate: { opacity: 1, y: 0, transition: { duration: 0.6, ease: 'easeOut' } },
exit: { opacity: 0, y: -20, transition: { duration: 0.3, ease: 'easeIn' } },
}

export const FadeIn = ({ children }: { children: React.ReactNode }) => {
return (
<motion.div
initial="initial"
animate="animate"
exit="exit"
variants={fadeInVariant}
>
{children}
</motion.div>
)
}

Now, any component wrapped in <FadeIn> will animate when mounted and unmount when removed.

Why Use Reusable Components?

  1. Consistency: Use the same animation across your app without duplicating code.
  2. Maintainability: Centralised animation logic makes updates easier.
  3. Performance: Easier to debug and tweak animations as your app grows.

Creating a Library of Animation Components

Let’s create a few more reusable animation components:

1. Slide In From Left

Let’s create an engaging entrance and exit animation where an element slides in from the left side of the screen and exits in the same direction. This can be achieved by defining a variant with three animation states. Initially, the element starts off-screen at x: -100 and is fully transparent (opacity: 0). In the animate state, it smoothly moves to its natural position at x: 0 while fading in (opacity: 1). When the component unmounts, the exit state reverses the animation, sliding the element back to the left and fading it out again.

This subtle yet effective motion adds a dynamic feel to your UI and is especially useful for animating sidebars, onboarding steps, tab transitions, or revealing content in a staggered layout. It enhances the user experience by providing visual continuity and context during screen changes or component updates. Plus, with reusable animation variants, you can easily apply this pattern across different parts of your application for consistent motion design.

// components/SlideInLeft.tsx
'use client'

import { motion } from “motion/react”

const slideLeft = {
initial: { x: -100, opacity: 0 },
animate: { x: 0, opacity: 1, transition: { duration: 0.5 } },
exit: { x: -100, opacity: 0, transition: { duration: 0.3 } },
}


export const SlideInLeft = ({ children }: { children: React.ReactNode }) => {
return (
<motion.div
initial="initial"
animate="animate"
exit="exit"
variants={slideLeft}
>
{children}
</motion.div>
)
}Now, any component wrapped in <FadeIn> will animate when mounted and unmount when removed.

2. Scale In with Delay

In this example, we’ll create a Scale-In with Delay animation to make elements appear with a smooth and attention-grabbing effect. The animation starts with the component slightly scaled down (scale: 0.95) and fully transparent (opacity: 0). On animate, it scales up to its full size (scale: 1) while fading in (opacity: 1). To enhance the visual flow, we add a short delay of 0.2 seconds, giving it a staggered appearance when used in a sequence.

This animation is ideal for drawing user attention to key UI elements like buttons, modals, alerts, or featured cards. The subtle scaling effect adds a sense of liveliness and focus, making it especially useful in onboarding flows, hero sections, or call-to-action areas. It's a simple yet elegant motion that brings your interface to life.

// components/ScaleIn.tsx
'use client'

import { motion } from “motion/react”

const scaleIn = {
initial: { scale: 0.95, opacity: 0 },
animate: {
scale: 1,
opacity: 1,
transition: { delay: 0.2, duration: 0.4, ease: 'easeOut' },
},
}

export const ScaleIn = ({ children }: { children: React.ReactNode }) => {
return (
<motion.div initial="initial" animate="animate" variants={scaleIn}>
{children}
</motion.div>
)
}

Composing Animations with Props

Let’s take our animation a step further by making it more dynamic and customizable. In this advanced version of the fade-in effect, we allow the animation to accept delay and duration as props. This lets each instance control its timing, making it easy to adjust how and when elements appear. The variant is generated dynamically using the values passed in, giving you fine-grained control over the animation behaviour.

This approach is especially powerful when rendering multiple items—such as in a list or grid—where you want each element to animate in with staggered timing. It also works great for scenarios where the animation should adapt based on user interactions or component state changes. By making your variants flexible, you unlock a wide range of motion patterns tailored to your UI needs.

// components/FadeInDynamic.tsx
'use client'

import { motion, Variant } from “motion/react”

interface FadeInProps {
children: React.ReactNode
delay?: number
duration?: number
}

export const FadeInDynamic = ({ children, delay = 0, duration = 0.6 }: FadeInProps) => {
const variant: Variant = {
initial: { opacity: 0, y: 20 },
animate: {
opacity: 1,
y: 0,
transition: { duration, delay, ease: 'easeOut' },
},
}

return (
<motion.div initial="initial" animate="animate" variants={variant}>
{children}
</motion.div>
)
}

Using Reusable Animations in a Page

Now let’s use all the animation components in a real Next.js page. Just import them and wrap your UI elements inside:

// app/page.tsx
"use client";
import {AnimatePresence, motion} from "motion/react";
import {FadeIn} from "@/components/FadeIn";
import {ScaleIn} from "@/components/ScaleIn";
import {SlideInLeft} from "@/components/SlideInLeft";
import {useState} from "react";


export default function Home() {
const [visible, setVisible] = useState(true);
return (
<main className="p-10 space-y-6">
<AnimatePresence mode="wait">
{visible && (
<>
<FadeIn>
<h1 className="text-4xl font-bold">Welcome to My Site</h1>
</FadeIn>


<SlideInLeft>
<p className="text-gray-600">This text slides in from the left.</p>
</SlideInLeft>
</>
)}
</AnimatePresence>
<ScaleIn>
<button
className="px-4 py-2 bg-blue-500 text-white rounded"
onClick={() => setVisible((prev) => !prev)}
>
Click Me
</button>
</ScaleIn>
</main>
);
}

Tips for Building More Animations

  • Use AnimatePresence from motion when you want exit animations to work properly, especially when conditionally rendering components.
  • Try motion.section, motion.article, or any semantic tag with motion for better accessibility and structure.
  • Combine these with useInView() to animate items only when they scroll into view.
  • Use the layout prop for smooth transitions in lists or reorderable content.

Conclusion

Creating reusable animation components in Next.js using the latest Motion library offers a clean and efficient way to craft engaging, interactive user interfaces. By leveraging variants, your animations become consistent, predictable, and easy to manage.

This modular approach not only enhances the visual appeal of your project but also boosts scalability and maintainability, making your Next.js app feel more dynamic and polished, whether it’s a portfolio, landing page, or dashboard.


Enhance your website’s smoothness and visual appeal to elevate the user experience—contact us, and we’ll guide you every step of the way.

footer-particlefooter-particlefooter-particlefooter-particlefooter-particle
back-to-top