React Transition Group
Introduction
Making smooth transitions between different UI states is a crucial aspect of modern web applications. React Transition Group is a popular library that helps React developers add transitions to elements as they enter or leave the DOM. It provides a set of components that make it easier to define and control transition effects.
React Transition Group isn't an animation library like React Spring or Framer Motion. Instead, it exposes transition stages that you can use to apply animations with CSS or other animation libraries. Think of it as a toolkit that helps manage the lifecycle of your components during transitions.
In this tutorial, we'll explore React Transition Group's core components, understand how they work, and build practical examples to demonstrate their usage.
Prerequisites
Before we begin, make sure you have:
- Basic knowledge of React
- A React project set up
- React Transition Group installed
To install React Transition Group, run:
npm install react-transition-group
# or
yarn add react-transition-group
Core Components
React Transition Group provides three main components:
Transition
- Low-level API for transitionsCSSTransition
- Builds onTransition
to make implementing CSS transitions easierTransitionGroup
- Manages a list of multiple transition components
Let's explore each of these components in detail.
The Transition
Component
The Transition
component is the core component that manages component states during transitions. It doesn't apply any styles by itself but gives you control over different transition phases.
Transition States
A transition goes through four states:
entering
: Starting state when the component is about to enterentered
: Final state when the component has fully enteredexiting
: Starting state when the component is about to exitexited
: Final state when the component has fully exited
Basic Example
import { useState } from 'react';
import { Transition } from 'react-transition-group';
const duration = 300;
const defaultStyle = {
transition: `opacity ${duration}ms ease-in-out`,
opacity: 0,
}
const transitionStyles = {
entering: { opacity: 1 },
entered: { opacity: 1 },
exiting: { opacity: 0 },
exited: { opacity: 0 },
};
function TransitionExample() {
const [inProp, setInProp] = useState(false);
return (
<div>
<button onClick={() => setInProp(prevState => !prevState)}>
Toggle
</button>
<Transition in={inProp} timeout={duration}>
{state => (
<div style={{
...defaultStyle,
...transitionStyles[state]
}}>
I'm a fading element!
</div>
)}
</Transition>
</div>
);
}
In this example:
- We use a state variable
inProp
to control whether the element should be shown. - The
in
prop determines whether the component is in an "entered" or "exited" state. - The
timeout
prop specifies how long the transition should take. - We provide a function as a child that receives the current transition state and returns a component with appropriate styles.
The CSSTransition
Component
While Transition
gives you complete control, it requires manual styling. CSSTransition
makes this more convenient by automatically applying and removing CSS classes during the transition phases.
CSS Classes
CSSTransition
adds these classes to your component:
{classPrefix}-enter
: Applied immediately when the component mounts{classPrefix}-enter-active
: Applied one frame after the component mounts{classPrefix}-enter-done
: Applied when the entering transition is completed{classPrefix}-exit
: Applied when the exit transition is triggered{classPrefix}-exit-active
: Applied one frame after the exit transition is triggered{classPrefix}-exit-done
: Applied when the exiting transition is completed
Basic Example
import { useState } from 'react';
import { CSSTransition } from 'react-transition-group';
import './styles.css'; // We'll define our CSS here
function CSSTransitionExample() {
const [isVisible, setIsVisible] = useState(false);
return (
<div>
<button onClick={() => setIsVisible(prevState => !prevState)}>
Toggle
</button>
<CSSTransition
in={isVisible}
timeout={300}
classNames="fade"
unmountOnExit
>
<div className="box">
I'll fade in and out!
</div>
</CSSTransition>
</div>
);
}
And here's the corresponding CSS:
/* styles.css */
.box {
padding: 20px;
margin-top: 20px;
background-color: #3498db;
color: white;
border-radius: 4px;
}
/* Enter transitions */
.fade-enter {
opacity: 0;
}
.fade-enter-active {
opacity: 1;
transition: opacity 300ms;
}
/* Exit transitions */
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0;
transition: opacity 300ms;
}
In this example:
- We use the
classNames
prop to define the prefix for our transition classes. - The
unmountOnExit
prop ensures the component is removed from the DOM after exiting. - Our CSS defines how the element should appear during each transition phase.
The TransitionGroup
Component
While Transition
and CSSTransition
handle single elements, TransitionGroup
manages a collection of transition components, making it perfect for lists where items may be added or removed.
List Example
import { useState } from 'react';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import './list-styles.css';
function ListExample() {
const [items, setItems] = useState([
{ id: 1, text: 'Buy groceries' },
{ id: 2, text: 'Learn React' },
{ id: 3, text: 'Build amazing app' },
]);
const [nextId, setNextId] = useState(4);
const [inputText, setInputText] = useState('');
const addItem = () => {
if (inputText.trim() !== '') {
setItems([...items, { id: nextId, text: inputText }]);
setNextId(nextId + 1);
setInputText('');
}
};
const removeItem = (id) => {
setItems(items.filter(item => item.id !== id));
};
return (
<div>
<div className="controls">
<input
value={inputText}
onChange={(e) => setInputText(e.target.value)}
placeholder="Add a new task"
/>
<button onClick={addItem}>Add</button>
</div>
<TransitionGroup className="task-list">
{items.map(item => (
<CSSTransition
key={item.id}
timeout={500}
classNames="task"
>
<div className="task-item">
<span>{item.text}</span>
<button onClick={() => removeItem(item.id)}>X</button>
</div>
</CSSTransition>
))}
</TransitionGroup>
</div>
);
}
And the corresponding CSS:
/* list-styles.css */
.task-list {
margin-top: 20px;
}
.task-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
margin-bottom: 10px;
background-color: #f8f9fa;
border-radius: 4px;
}
/* Enter transitions */
.task-enter {
opacity: 0;
transform: translateX(-20px);
}
.task-enter-active {
opacity: 1;
transform: translateX(0);
transition: opacity 500ms, transform 500ms;
}
/* Exit transitions */
.task-exit {
opacity: 1;
}
.task-exit-active {
opacity: 0;
transform: translateX(20px);
transition: opacity 500ms, transform 500ms;
}
In this example:
TransitionGroup
wraps our list of tasks.- Each task is wrapped in a
CSSTransition
component. - When tasks are added or removed, the appropriate transition effects are applied.
Real-World Example: Animated Modal
Let's create a more practical example - an animated modal dialog:
import { useState } from 'react';
import { CSSTransition } from 'react-transition-group';
import './modal.css';
function AnimatedModal() {
const [showModal, setShowModal] = useState(false);
return (
<div className="modal-example">
<button onClick={() => setShowModal(true)}>Open Modal</button>
<CSSTransition
in={showModal}
timeout={300}
classNames="modal"
unmountOnExit
>
<div className="modal-overlay">
<div className="modal-content">
<h2>Welcome to our Modal!</h2>
<p>This is a modal with smooth enter and exit animations powered by React Transition Group.</p>
<button onClick={() => setShowModal(false)}>Close</button>
</div>
</div>
</CSSTransition>
</div>
);
}
CSS for the modal:
/* modal.css */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal-content {
background-color: white;
padding: 20px;
border-radius: 8px;
max-width: 500px;
width: 90%;
}
/* Enter transitions */
.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: translateY(0);
transition: transform 300ms;
}
/* Exit transitions */
.modal-exit {
opacity: 1;
}
.modal-exit .modal-content {
transform: scale(1);
}
.modal-exit-active {
opacity: 0;
transition: opacity 300ms;
}
.modal-exit-active .modal-content {
transform: scale(0.9);
transition: transform 300ms;
}
This modal example demonstrates how to create a practical UI component with smooth entrance and exit animations. When the modal opens, it fades in and slightly scales up. When it closes, it fades out and scales down.
Advanced Usage: Transition Hooks
CSSTransition
provides hooks for various stages of the transition, which can be useful for executing code at specific points:
import { useState, useRef } from 'react';
import { CSSTransition } from 'react-transition-group';
function AdvancedExample() {
const [isVisible, setIsVisible] = useState(false);
const nodeRef = useRef(null);
return (
<div>
<button onClick={() => setIsVisible(!isVisible)}>
Toggle Element
</button>
<CSSTransition
in={isVisible}
timeout={500}
classNames="fade"
nodeRef={nodeRef}
unmountOnExit
onEnter={() => console.log('Enter started')}
onEntering={() => console.log('Entering...')}
onEntered={() => console.log('Enter completed')}
onExit={() => console.log('Exit started')}
onExiting={() => console.log('Exiting...')}
onExited={() => console.log('Exit completed')}
>
<div ref={nodeRef} className="box">
I'm using transition hooks!
</div>
</CSSTransition>
</div>
);
}
Note the use of nodeRef
here, which is the recommended approach in React 18+ to avoid using findDOMNode.
Performance Considerations
When working with React Transition Group, keep these performance tips in mind:
- Use the
nodeRef
prop to avoid the deprecatedfindDOMNode
method. - For large lists, consider virtualizing them with libraries like
react-window
while still using transitions. - Keep transition durations reasonable - long animations can feel sluggish.
- Be careful with complex CSS transforms that might cause layout thrashing.
Flow of a Transition
Let's visualize how a transition works with React Transition Group:
Summary
React Transition Group provides a flexible way to add animations to your React applications. We've explored its three main components:
Transition
: The low-level component that gives you control over transition statesCSSTransition
: Simplifies working with CSS transitions by automatically applying and removing classesTransitionGroup
: Manages lists of transitioning components
By leveraging these components, you can create smooth, engaging user interfaces with minimal effort. Remember that React Transition Group is primarily a state machine for transitions - you'll still need to define the actual animations using CSS or another animation library.
Additional Resources
To continue learning about animations in React:
- React Transition Group Documentation
- Explore other animation libraries like React Spring or Framer Motion for more advanced animations
- Check out CSS animations and transitions on MDN for a deeper understanding of the underlying technologies
Exercises
- Create a dropdown menu that smoothly slides in and out when toggled.
- Build a tabbed interface where the content fades between different tabs.
- Implement a notification system where alerts slide in from the top of the screen and fade out after a few seconds.
- Create an image carousel with smooth transitions between images.
- Enhance the todo list example to animate not just additions and removals, but also completion status changes.
Happy animating!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)