Add Smooth Animations to Your Next.js App with Framer Motion & Tailwind CSS
As a front-end developer, you know that a static user interface, no matter how functional, can sometimes feel lifeless. Users expect dynamic, responsive experiences, and adding smooth animations is key to delivering that polish. This tutorial will guide you through integrating Framer Motion and Tailwind CSS into your Next.js application to create professional, engaging micro-interactions and transitions that elevate your UI.
Why Animate Your UI? The Power of Micro-interactions
Animations are more than just visual flair; they are crucial for improving user experience. Subtle movements, known as micro-interactions, guide users, provide feedback, and make an application feel more intuitive and enjoyable. With Framer Motion, you can easily add these sophisticated touches. We will learn how to use Framer Motion to add micro-interactions like hover animations, smooth transitions, and fade-ins. These modern touches significantly enhance the user experience, making your application feel premium and responsive.
Imagine a button that subtly scales when hovered over, or a list of items that gracefully fades into view one by one. These aren't just cosmetic changes; they communicate functionality and delight the user. Combining Framer Motion's declarative API with Tailwind CSS's utility-first approach makes implementing these effects efficient and maintainable.
Setting Up Your Project with Framer Motion and Tailwind CSS
Before we dive into animations, let's ensure your Next.js project is set up with Tailwind CSS and Framer Motion. If you already have a Next.js project with Tailwind CSS configured, you can skip to step 3.
- Create a New Next.js Project:
Follow the prompts. For this tutorial, we'll use the App Router.npx create-next-app@latest my-animated-app --typescript --eslint --tailwind --app - Verify Tailwind CSS Setup:
Next.js's setup command includes Tailwind CSS. Ensure your
tailwind.config.jsandglobals.cssfiles are correctly configured. You should see Tailwind directives inglobals.css:@tailwind base; @tailwind components; @tailwind utilities; - Install Framer Motion:
Add Framer Motion to your project dependencies:
ornpm install framer-motionyarn add framer-motion
Your project is now ready to begin adding dynamic animations. If you're looking to integrate other powerful tools with Next.js, consider learning how to connect Sanity CMS with Next.js 14 for a robust content management solution.
Your First Animation: Fading in Elements on Load with Framer Motion
Let's start with a classic: making an element fade in smoothly when it appears on the screen. This is a common effect for headings, paragraphs, and other UI components to give a polished entry.
Framer Motion provides the motion component, which is a wrapper around standard HTML or React components (like div, span, button, etc.) that enables animation capabilities. We'll use motion.div for our heading.
Here's how to make a heading fade in from slightly below its final position:
import { motion } from 'framer-motion';
export default function Home() {
// Define animation variants
const fadeInAnimationVariants = {
initial: {
opacity: 0,
y: 10, // Start slightly below
},
animate: {
opacity: 1,
y: 0, // End at original position
transition: {
duration: 0.6,
ease: "easeOut",
},
},
};
return (
<div className="flex min-h-screen flex-col items-center justify-center p-24 bg-gray-900 text-white">
<motion.h1
className="text-5xl font-bold mb-4"
variants={fadeInAnimationVariants}
initial="initial"
animate="animate"
>
Welcome to Juno School!
</motion.h1>
<motion.p
className="text-xl"
variants={fadeInAnimationVariants}
initial="initial"
animate="animate"
transition={{ delay: 0.2 }} // Add a slight delay for the paragraph
>
Learn professional skills for free.
</motion.p>
</div>
);
}
In this example, for the heading, we set the initial condition to an opacity of 0 and a y-position of 10. Then, in the animate prop, we set opacity to 1 and y to 0. Finally, we add a transition for a smooth effect. This creates a subtle fade-in-from-bottom animation. We also applied the same variants to a paragraph, adding a delay to its transition for a staggered effect.
Creating Interactive Hover and Tap Effects with Framer Motion
Micro-interactions truly shine when elements respond to user input. Framer Motion makes it simple to add interactive effects like scaling on hover or tap. Let's create an interactive button.
import { motion } from 'framer-motion';
export default function InteractiveButton() {
return (
<div className="flex min-h-screen flex-col items-center justify-center bg-gray-900 p-24">
<motion.button
className="px-8 py-4 bg-blue-600 text-white font-semibold rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-75"
whileHover={{ scale: 1.1 }} // Scale up on hover
whileTap={{ scale: 0.95 }} // Scale down on tap
transition={{ type: "spring", stiffness: 400, damping: 10 }}
>
Click Me!
</motion.button>
</div>
);
}
Here, we've used a motion.button and set whileHover to scale it slightly to 1.1, making the button appear a little larger when the user hovers over it. For whileTap, we've scaled it to 0.95, giving it a slight compression effect when clicked. The transition prop with a "spring" type makes the animation feel natural and bouncy, a hallmark of excellent smooth animation techniques.
Orchestrating Animations with Staggering
When you have multiple elements, like items in a list or components in a hero section, animating them all at once can look clunky. Staggering introduces a slight delay between each element's animation, creating a pleasing cascade effect that feels organized and professional.
Framer Motion uses variants and a parent motion component to orchestrate these effects. The parent defines the overall animation, and its children inherit and apply their own delays.
import { motion } from 'framer-motion';
export default function StaggeredList() {
const containerVariants = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.1, // Delay between each child's animation
},
},
};
const itemVariants = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0 },
};
const listItems = ["Learn React", "Build Projects", "Get Certified", "Join Juno"];
return (
<div className="flex min-h-screen flex-col items-center justify-center bg-gray-900 p-24 text-white">
<h2 className="text-3xl font-bold mb-6">Our Learning Path</h2>
<motion.ul
variants={containerVariants}
initial="hidden"
animate="show"
className="list-disc list-inside text-xl space-y-2"
>
{listItems.map((item, index) => (
<motion.li key={index} variants={itemVariants}>
{item}
</motion.li>
))}
</motion.ul>
</div>
);
}
In this code, the containerVariants define a staggerChildren property, which tells Framer Motion to apply a 0.1-second delay between each child's animation. The itemVariants then define how each individual list item animates. This creates a smooth, cascading entrance for the list items, significantly enhancing the overall aesthetic and user perception of "modern touches" in the UI.
Putting It All Together: Animating a Full Hero Section with Framer Motion and Tailwind CSS
Now, let's combine all the techniques we've learned to create a fully animated hero section. We'll have text fading in, an image appearing with a delay, and an interactive button.
import { motion } from 'framer-motion';
import Image from 'next/image'; // Assuming Next.js Image component
export default function AnimatedHeroSection() {
const containerVariants = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.15, // Stagger children slightly
},
},
};
const textVariants = {
hidden: { opacity: 0, y: 20 },
show: {
opacity: 1,
y: 0,
transition: {
duration: 0.6,
ease: "easeOut",
},
},
};
const imageVariants = {
hidden: { opacity: 0, scale: 0.8 },
show: {
opacity: 1,
scale: 1,
transition: {
delay: 0.5, // Image appears after text
duration: 0.8,
ease: "easeOut",
},
},
};
return (
<motion.section
className="relative flex flex-col md:flex-row items-center justify-center min-h-screen bg-gradient-to-br from-gray-900 to-gray-800 text-white p-8 md:p-16 overflow-hidden"
variants={containerVariants}
initial="hidden"
animate="show"
>
{/* Background element for subtle animation */}
<motion.div
className="absolute inset-0 z-0 bg-blue-500 opacity-10 blur-3xl"
animate={{
scale: [1, 1.05, 1],
rotate: [0, 5, -5, 0],
}}
transition={{
duration: 10,
repeat: Infinity,
repeatType: "reverse",
ease: "easeInOut",
}}
/>
<div className="relative z-10 flex flex-col md:w-1/2 text-center md:text-left mb-8 md:mb-0">
<motion.h1
className="text-5xl md:text-6xl font-extrabold mb-4 leading-tight"
variants={textVariants}
>
Unlock Your Potential with Juno School
</motion.h1>
<motion.p
className="text-lg md:text-xl mb-8 text-gray-300 max-w-md md:max-w-none mx-auto"
variants={textVariants}
>
Explore a world of free, professional certificate courses designed for your career growth.
</motion.p>
<motion.button
className="px-8 py-4 bg-purple-600 text-white font-bold rounded-full shadow-lg hover:bg-purple-700 transition-colors duration-300 self-center md:self-start"
whileHover={{ scale: 1.05, boxShadow: "0px 10px 20px rgba(0,0,0,0.3)" }}
whileTap={{ scale: 0.98 }}
transition={{ type: "spring", stiffness: 300, damping: 15 }}
variants={textVariants} // Use text variants for initial appearance
>
Start Learning Today
</motion.button>
</div>
<motion.div
className="relative z-10 md:w-1/2 flex justify-center items-center"
variants={imageVariants}
>
<Image
src="/hero-image.png" // Replace with your actual image path
alt="Student learning on laptop"
width={600}
height={400}
className="rounded-xl shadow-2xl"
/>
</motion.div>
</motion.section>
);
}
In this comprehensive hero section, we've applied several Framer Motion concepts:
- A parent
motion.sectionusescontainerVariantsto orchestrate the entry of its children. - The heading and paragraph use
textVariants, leveraging the initial opacity and y-position animation we explored earlier. - The button incorporates
whileHoverandwhileTapfor interactive feedback, similar to our previous example, but with slightly adjusted styling and spring properties. - The image uses
imageVariants, which includes adelayin its transition, ensuring it appears after the main text, creating a smooth visual flow. - A subtle background animation is added using
animatewith keyframes for scale and rotation, giving a dynamic feel to the entire section.
By using Framer Motion and Tailwind CSS, you can build not just functional, but also beautiful and engaging user interfaces in your Next.js applications. These techniques are part of a broader set of skills covered in Juno's Web Development Full Course in Hindi, where you can learn to master modern web technologies.
Ready to level up your career?
Join 5 lakh+ learners on the Juno app. Certificate courses in Hindi and English.