Skip to main content

Next.js Animation

Animation can significantly enhance user experience in web applications by providing visual feedback, guiding attention, and making interfaces more engaging. In this section, we'll explore various ways to add animations to your Next.js applications.

Introduction to Animations in Next.js

Next.js doesn't have built-in animation features, but it works seamlessly with a variety of animation libraries and CSS techniques. This flexibility allows developers to choose the best approach for their specific needs. We'll cover:

  1. CSS-based animations
  2. Using Framer Motion library
  3. React Spring integration
  4. Animating page transitions

CSS-based Animations in Next.js

Basic CSS Animations

CSS animations are the simplest way to add motion to your components. They work in Next.js just like in any other React application.

css
/* styles/animations.css */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}

.fade-in {
animation: fadeIn 1s ease-in-out;
}

To use this animation in your Next.js component:

jsx
import '../styles/animations.css';

function AnimatedComponent() {
return (
<div className="fade-in">
This content will fade in!
</div>
);
}

CSS Transitions

CSS transitions provide a simpler way to animate changes in CSS properties:

jsx
// components/HoverButton.js
import { useState } from 'react';

function HoverButton() {
const [isHovered, setIsHovered] = useState(false);

return (
<button
style={{
padding: '10px 20px',
backgroundColor: isHovered ? '#2563eb' : '#3b82f6',
color: 'white',
border: 'none',
borderRadius: '4px',
transition: 'background-color 0.3s ease',
cursor: 'pointer'
}}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
Hover Me
</button>
);
}

CSS Modules with Animations

Next.js has built-in support for CSS Modules, which can be used for component-scoped animations:

css
/* Button.module.css */
.button {
background-color: #3b82f6;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
transition: transform 0.3s ease, background-color 0.3s ease;
}

.button:hover {
transform: scale(1.05);
background-color: #2563eb;
}
jsx
// components/AnimatedButton.js
import styles from './Button.module.css';

function AnimatedButton() {
return (
<button className={styles.button}>
Animated Button
</button>
);
}

Framer Motion in Next.js

Framer Motion is a popular animation library for React that provides a powerful yet simple API.

Installation

First, install Framer Motion:

bash
npm install framer-motion
# or
yarn add framer-motion

Basic Usage

jsx
// components/AnimatedBox.js
import { motion } from 'framer-motion';

function AnimatedBox() {
return (
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.5 }}
style={{
width: 100,
height: 100,
backgroundColor: '#3b82f6'
}}
/>
);
}

Gestures with Framer Motion

Framer Motion makes it easy to add gesture-based animations:

jsx
// components/DraggableCard.js
import { motion } from 'framer-motion';

function DraggableCard() {
return (
<motion.div
drag
dragConstraints={{
top: -50,
left: -50,
right: 50,
bottom: 50,
}}
whileDrag={{ scale: 1.1 }}
style={{
width: 150,
height: 150,
backgroundColor: '#f472b6',
borderRadius: 10,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
color: 'white',
fontWeight: 'bold',
cursor: 'grab'
}}
>
Drag me!
</motion.div>
);
}

Exit Animations

Framer Motion can animate elements when they're removed from the DOM:

jsx
// components/AnimatedList.js
import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';

function AnimatedList() {
const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3']);

const removeItem = (index) => {
setItems(items.filter((_, i) => i !== index));
};

return (
<div>
<ul style={{ listStyleType: 'none', padding: 0 }}>
<AnimatePresence>
{items.map((item, index) => (
<motion.li
key={item}
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
style={{ marginBottom: 10 }}
>
<div style={{
padding: 15,
backgroundColor: '#e0e7ff',
borderRadius: 4,
display: 'flex',
justifyContent: 'space-between'
}}>
{item}
<button onClick={() => removeItem(index)}>Remove</button>
</div>
</motion.li>
))}
</AnimatePresence>
</ul>
<button onClick={() => setItems([...items, `Item ${items.length + 1}`])}>
Add Item
</button>
</div>
);
}

Page Transitions in Next.js

Using Framer Motion for Page Transitions

You can create smooth transitions between pages by using Framer Motion with Next.js:

jsx
// pages/_app.js
import { AnimatePresence } from 'framer-motion';
import '../styles/globals.css';

function MyApp({ Component, pageProps, router }) {
return (
<AnimatePresence mode="wait" initial={false}>
<Component {...pageProps} key={router.asPath} />
</AnimatePresence>
);
}

export default MyApp;

Then, wrap your page content in a motion component:

jsx
// pages/about.js
import { motion } from 'framer-motion';

const variants = {
hidden: { opacity: 0, x: -200 },
enter: { opacity: 1, x: 0 },
exit: { opacity: 0, x: 200 },
};

export default function About() {
return (
<motion.div
variants={variants}
initial="hidden"
animate="enter"
exit="exit"
transition={{ duration: 0.4 }}
>
<h1>About Page</h1>
<p>This page will animate in and out.</p>
</motion.div>
);
}

React Spring in Next.js

React Spring is another popular animation library that uses physics-based animations.

Installation

bash
npm install @react-spring/web
# or
yarn add @react-spring/web

Basic Usage

jsx
// components/SpringAnimation.js
import { useSpring, animated } from '@react-spring/web';
import { useState } from 'react';

function SpringAnimation() {
const [flipped, setFlipped] = useState(false);

const props = useSpring({
transform: flipped ? 'rotateY(180deg)' : 'rotateY(0deg)',
config: { mass: 5, tension: 500, friction: 80 }
});

return (
<div>
<animated.div
style={{
...props,
width: 200,
height: 200,
backgroundColor: '#34d399',
borderRadius: 8,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'white',
fontWeight: 'bold'
}}
>
Click me to flip
</animated.div>

<button
onClick={() => setFlipped(!flipped)}
style={{ marginTop: 20 }}
>
Toggle Flip
</button>
</div>
);
}

Real-world Example: Animated Notification System

Let's create a notification system that animates notifications in and out:

jsx
// components/NotificationSystem.js
import { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';

// Notification component
const Notification = ({ message, type, id, onClose }) => {
useEffect(() => {
const timer = setTimeout(() => {
onClose(id);
}, 5000);

return () => clearTimeout(timer);
}, [id, onClose]);

const bgColors = {
success: '#10b981',
error: '#ef4444',
warning: '#f59e0b',
info: '#3b82f6'
};

return (
<motion.div
layout
initial={{ opacity: 0, y: -50, scale: 0.8 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, scale: 0.8, transition: { duration: 0.2 } }}
style={{
padding: '12px 16px',
borderRadius: 4,
marginBottom: 8,
color: 'white',
width: 300,
position: 'relative',
backgroundColor: bgColors[type] || bgColors.info
}}
>
<p>{message}</p>
<button
onClick={() => onClose(id)}
style={{
position: 'absolute',
top: 5,
right: 5,
background: 'none',
border: 'none',
color: 'white',
cursor: 'pointer'
}}
>
&times;
</button>
</motion.div>
);
};

// Notification container
export default function NotificationSystem() {
const [notifications, setNotifications] = useState([]);

const addNotification = (type) => {
const id = Date.now();
const messages = {
success: 'Operation completed successfully!',
error: 'Error: Something went wrong!',
warning: 'Warning: This might cause issues.',
info: 'Just so you know...'
};

setNotifications([
...notifications,
{ id, type, message: messages[type] }
]);
};

const removeNotification = (id) => {
setNotifications(notifications.filter(notification => notification.id !== id));
};

return (
<div>
<div style={{ marginBottom: 20 }}>
<button onClick={() => addNotification('success')} style={{ marginRight: 8 }}>
Success Message
</button>
<button onClick={() => addNotification('error')} style={{ marginRight: 8 }}>
Error Message
</button>
<button onClick={() => addNotification('warning')} style={{ marginRight: 8 }}>
Warning Message
</button>
<button onClick={() => addNotification('info')}>
Info Message
</button>
</div>

<div style={{
position: 'fixed',
top: 20,
right: 20,
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-end'
}}>
<AnimatePresence>
{notifications.map(notification => (
<Notification
key={notification.id}
{...notification}
onClose={removeNotification}
/>
))}
</AnimatePresence>
</div>
</div>
);
}

Performance Considerations

When using animations in Next.js, keep these performance tips in mind:

  1. Use hardware-accelerated properties: Stick to animating transform and opacity when possible, as these can be hardware-accelerated.

  2. Use will-change sparingly: The will-change CSS property tells the browser to prepare for an animation, but overusing it can lead to performance issues.

css
.will-animate {
will-change: transform, opacity;
/* Use only for elements that will animate frequently */
}
  1. Consider reduced motion preferences: Some users may prefer reduced motion for accessibility reasons.
jsx
// components/MotionAwareAnimation.js
import { useReducedMotion } from 'framer-motion';
import { motion } from 'framer-motion';

function MotionAwareAnimation() {
const shouldReduceMotion = useReducedMotion();

const variants = {
hidden: { opacity: 0, x: shouldReduceMotion ? 0 : -100 },
visible: { opacity: 1, x: 0 }
};

return (
<motion.div
variants={variants}
initial="hidden"
animate="visible"
>
Motion-aware content
</motion.div>
);
}

Summary

In this section, we've explored various ways to add animations to your Next.js applications:

  • CSS-based animations using transitions, keyframes, and CSS Modules
  • Using Framer Motion for complex animations and gestures
  • Implementing React Spring for physics-based animations
  • Creating page transitions in Next.js
  • Building a real-world animated notification system
  • Considering performance optimizations for smoother animations

Animations can greatly improve the user experience of your application when used thoughtfully. Start with simple animations and gradually incorporate more complex ones as you become more comfortable with the techniques.

Additional Resources

Exercises

  1. Create a loading spinner animation using CSS keyframes.
  2. Implement a photo gallery with image transition effects using Framer Motion.
  3. Build a dropdown menu that animates open and closed.
  4. Create an animated modal that fades in and scales when it appears.
  5. Implement page transitions for your Next.js application using your preferred animation library.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)