Skip to main content

Installation

Install React Transition Group via npm or yarn:
npm install react-transition-group
For TypeScript projects, also install type definitions:
npm install --save-dev @types/react-transition-group

Basic usage

React Transition Group provides low-level components for managing transitions. It doesn’t animate styles directly but exposes transition stages you can hook into.

CSSTransition

The most commonly used component, which applies CSS classes at different stages:
import { CSSTransition } from 'react-transition-group'
import { useState } from 'react'
import './styles.css'

function FadeTransition() {
  const [show, setShow] = useState(false)

  return (
    <>
      <button onClick={() => setShow(!show)}>Toggle</button>
      
      <CSSTransition
        in={show}
        timeout={300}
        classNames="fade"
        unmountOnExit
      >
        <div className="box">I will fade in and out</div>
      </CSSTransition>
    </>
  )
}
Corresponding CSS:
styles.css
/* Entering */
.fade-enter {
  opacity: 0;
}
.fade-enter-active {
  opacity: 1;
  transition: opacity 300ms;
}

/* Exiting */
.fade-exit {
  opacity: 1;
}
.fade-exit-active {
  opacity: 0;
  transition: opacity 300ms;
}

Transition lifecycle

Applied when component enters:
  1. {classNames}-enter - Initial state
  2. {classNames}-enter-active - Active transition
  3. {classNames}-enter-done - Completed

Key features

CSS-based

Uses CSS transitions and animations for actual styling, keeping JavaScript minimal.

Lifecycle hooks

Provides callbacks for transition stages: onEnter, onEntering, onEntered, onExit, onExiting, onExited.

TransitionGroup

Automatically manages transitions for lists of elements.

Lightweight

Small footprint focused solely on managing transition states.

TransitionGroup for lists

Manage transitions for dynamic lists:
import { TransitionGroup, CSSTransition } from 'react-transition-group'
import { useState } from 'react'

function TodoList() {
  const [items, setItems] = useState(['Item 1', 'Item 2'])
  
  const addItem = () => {
    setItems([...items, `Item ${items.length + 1}`])
  }
  
  const removeItem = (index: number) => {
    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="item">
              {item}
              <button onClick={() => removeItem(index)}>×</button>
            </div>
          </CSSTransition>
        ))}
      </TransitionGroup>
    </div>
  )
}

JavaScript-based transitions

Use the base Transition component for JavaScript animations:
import { Transition } from 'react-transition-group'
import { useRef } from 'react'

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 Fade({ in: inProp, children }) {
  const nodeRef = useRef(null)
  
  return (
    <Transition nodeRef={nodeRef} in={inProp} timeout={duration}>
      {state => (
        <div ref={nodeRef} style={{
          ...defaultStyle,
          ...transitionStyles[state]
        }}>
          {children}
        </div>
      )}
    </Transition>
  )
}

Common transition patterns

Slide in from side

.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;
}

Scale and fade

.scale-enter {
  opacity: 0;
  transform: scale(0.9);
}
.scale-enter-active {
  opacity: 1;
  transform: scale(1);
  transition: opacity 300ms, transform 300ms;
}
.scale-exit {
  opacity: 1;
  transform: scale(1);
}
.scale-exit-active {
  opacity: 0;
  transform: scale(0.9);
  transition: opacity 300ms, transform 300ms;
}

When to use it

Perfect when you prefer writing animations in CSS and just need JavaScript to manage transition timing.
Ideal for basic component mount/unmount animations without complex gesture support.
Great for animating items being added or removed from lists with TransitionGroup.
Best when you want to keep animation logic in CSS and minimize JavaScript bundle size.
Commonly used in older React codebases. Consider Framer Motion for new projects.

Comparison with other libraries

React Transition Group:
  • Lower level, more manual control
  • CSS-based animations
  • Smaller bundle size
  • No built-in gesture support
Framer Motion:
  • Higher level, more features
  • JavaScript-based animations
  • Built-in gestures and layout animations
  • More intuitive API

Migration tips

If you’re using React Transition Group and want to modernize:
1

Identify usage

Find all CSSTransition and TransitionGroup components in your codebase.
2

Choose replacement

Select Framer Motion for UI animations or React Spring for physics-based motion.
3

Migrate gradually

Both libraries can coexist. Migrate component by component.
4

Update tests

Ensure animation tests work with the new library’s API.

Learn more

Transition examples

See transition patterns in action

Official docs

Visit React Transition Group docs

Build docs developers (and LLMs) love