React Transitions
Adding smooth animations to React components
✨ What are React Transitions?
React transitions add smooth animations when components appear, update, or disappear. They enhance user experience by making interface changes feel natural and polished instead of abrupt and jarring.
import { CSSTransition } from 'react-transition-group';
function App() {
const [show, setShow] = useState(true);
return (
<CSSTransition in={show} timeout={300} classNames="fade">
<div>Hello!</div>
</CSSTransition>
);
}
Key Transition Concepts
CSSTransition
Apply CSS classes during transitions
<CSSTransition
in={show}
timeout={300}
/>
TransitionGroup
Manage multiple transitioning elements
<TransitionGroup>
{items.map(...)}
</TransitionGroup>
Timeout
Control animation duration
timeout={300}
// or
timeout={{
enter: 300,
exit: 200
}}
Class Names
Define transition CSS classes
.fade-enter {
opacity: 0;
}
.fade-enter-active {
opacity: 1;
}
🔹 Installing React Transition Group
React Transition Group is the official library for adding transitions to React components. It provides low-level components for defining entering and exiting transitions, making animations smooth and performant.
# Install react-transition-group
npm install react-transition-group
# or with yarn
yarn add react-transition-group
Note: This library works with CSS transitions and animations.
🔹 Basic Fade Transition
A fade transition smoothly changes opacity when elements appear or disappear. This is one of the most common and subtle animations, perfect for modals, tooltips, and notifications in your application.
// FadeExample.jsx
import { useState } from 'react';
import { CSSTransition } from 'react-transition-group';
import './fade.css';
function FadeExample() {
const [show, setShow] = useState(true);
return (
<div>
<button onClick={() => setShow(!show)}>
Toggle
</button>
<CSSTransition
in={show}
timeout={300}
classNames="fade"
unmountOnExit
>
<div className="box">
I fade in and out!
</div>
</CSSTransition>
</div>
);
}
/* fade.css */
.fade-enter {
opacity: 0;
}
.fade-enter-active {
opacity: 1;
transition: opacity 300ms ease-in;
}
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0;
transition: opacity 300ms ease-out;
}
.box {
padding: 20px;
background: #3498db;
color: white;
border-radius: 8px;
}
🔹 Slide Transition
Slide transitions move elements in from the side, creating a dynamic entrance effect. They're ideal for sidebars, drawers, and mobile menus, providing clear visual feedback about content appearing or disappearing.
// SlideExample.jsx
import { useState } from 'react';
import { CSSTransition } from 'react-transition-group';
import './slide.css';
function SlideExample() {
const [show, setShow] = useState(false);
return (
<div>
<button onClick={() => setShow(!show)}>
{show ? 'Hide' : 'Show'} Panel
</button>
<CSSTransition
in={show}
timeout={300}
classNames="slide"
unmountOnExit
>
<div className="panel">
<h3>Side Panel</h3>
<p>This slides in from the right!</p>
</div>
</CSSTransition>
</div>
);
}
/* slide.css */
.slide-enter {
transform: translateX(100%);
}
.slide-enter-active {
transform: translateX(0);
transition: transform 300ms ease-out;
}
.slide-exit {
transform: translateX(0);
}
.slide-exit-active {
transform: translateX(100%);
transition: transform 300ms ease-in;
}
.panel {
position: fixed;
right: 0;
top: 0;
width: 300px;
height: 100vh;
background: white;
box-shadow: -2px 0 8px rgba(0,0,0,0.1);
padding: 20px;
}
🔹 TransitionGroup for Lists
TransitionGroup manages transitions for multiple children, perfect for dynamic lists. It automatically handles adding and removing items with smooth animations, making list updates feel polished and professional in your application.
// ListExample.jsx
import { useState } from 'react';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import './list.css';
function ListExample() {
const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3']);
const addItem = () => {
const newItem = `Item ${items.length + 1}`;
setItems([...items, newItem]);
};
const removeItem = (index) => {
setItems(items.filter((_, i) => i !== index));
};
return (
<div>
<button onClick={addItem}>Add Item</button>
<TransitionGroup className="list">
{items.map((item, index) => (
<CSSTransition
key={item}
timeout={300}
classNames="item"
>
<div className="list-item">
{item}
<button onClick={() => removeItem(index)}>
Remove
</button>
</div>
</CSSTransition>
))}
</TransitionGroup>
</div>
);
}
/* list.css */
.item-enter {
opacity: 0;
transform: translateY(-20px);
}
.item-enter-active {
opacity: 1;
transform: translateY(0);
transition: all 300ms ease-out;
}
.item-exit {
opacity: 1;
transform: translateX(0);
}
.item-exit-active {
opacity: 0;
transform: translateX(100%);
transition: all 300ms ease-in;
}
.list-item {
padding: 15px;
margin: 10px 0;
background: #ecf0f1;
border-radius: 4px;
display: flex;
justify-content: space-between;
align-items: center;
}
🔹 Modal with Transition
Modals with transitions create professional overlay effects. Combining fade and scale animations makes dialogs appear smoothly, improving user experience and making your application feel more polished and responsive.
// Modal.jsx
import { CSSTransition } from 'react-transition-group';
import './modal.css';
function Modal({ show, onClose, children }) {
return (
<CSSTransition
in={show}
timeout={300}
classNames="modal"
unmountOnExit
>
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
{children}
<button onClick={onClose}>Close</button>
</div>
</div>
</CSSTransition>
);
}
// Usage
function App() {
const [showModal, setShowModal] = useState(false);
return (
<div>
<button onClick={() => setShowModal(true)}>
Open Modal
</button>
<Modal show={showModal} onClose={() => setShowModal(false)}>
<h2>Modal Title</h2>
<p>This is a modal with smooth transitions!</p>
</Modal>
</div>
);
}
/* modal.css */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
background: white;
padding: 30px;
border-radius: 8px;
max-width: 500px;
}
.modal-enter {
opacity: 0;
}
.modal-enter .modal-content {
transform: scale(0.9);
}
.modal-enter-active {
opacity: 1;
transition: opacity 300ms;
}
.modal-enter-active .modal-content {
transform: scale(1);
transition: transform 300ms;
}
.modal-exit {
opacity: 1;
}
.modal-exit-active {
opacity: 0;
transition: opacity 300ms;
}