React Transition Group

4.4.5 · active · verified Sun Apr 19

React Transition Group is a set of low-level primitive components for managing component states over time, specifically designed to facilitate animations in React applications. It enables developers to define and control lifecycle events for components entering, exiting, or remaining in the DOM. The current stable version is 4.4.5, last updated in August 2022, with a release cadence focused on stability and compatibility with React's evolving ecosystem. This library doesn't dictate specific animation libraries or CSS frameworks; instead, it provides hooks and class toggles (`CSSTransition`) that allow integration with arbitrary CSS transitions/animations or JavaScript animation libraries. Its key differentiator is providing an unopinionated, foundational API for animation patterns, contrasting with higher-level animation libraries that often bundle their own animation engines or opinions.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates animating a list of items entering and exiting using `TransitionGroup` and `CSSTransition`, applying basic CSS classes for fade effects. It also addresses the `nodeRef` requirement for React Strict Mode.

import React, { useState } from 'react';
import ReactDOM from 'react-dom/client';
import { CSSTransition, TransitionGroup } from 'react-transition-group';

// Basic CSS for demonstration (e.g., in App.css or a style tag)
/*
.item-enter {
  opacity: 0;
}
.item-enter-active {
  opacity: 1;
  transition: opacity 500ms ease-in;
}
.item-exit {
  opacity: 1;
}
.item-exit-active {
  opacity: 0;
  transition: opacity 500ms ease-out;
}
*/

interface TodoItemProps {
  todo: string;
  onRemove: () => void;
}

const TodoItem: React.FC<TodoItemProps> = ({ todo, onRemove }) => (
  <div className="todo-item" onClick={onRemove}>
    {todo}
  </div>
);

const App: React.FC = () => {
  const [todos, setTodos] = useState<string[]>(["Learn React", "Build something", "Deploy it"]);
  const [newTodo, setNewTodo] = useState<string>('');
  const nextId = React.useRef(todos.length);

  const handleAddTodo = () => {
    if (newTodo.trim() === '') return;
    setTodos([...todos, newTodo.trim()]);
    setNewTodo('');
    nextId.current++;
  };

  const handleRemoveTodo = (indexToRemove: number) => {
    setTodos(todos.filter((_, index) => index !== indexToRemove));
  };

  return (
    <div>
      <h1>Animated Todo List</h1>
      <input
        type="text"
        value={newTodo}
        onChange={(e) => setNewTodo(e.target.value)}
        onKeyDown={(e) => e.key === 'Enter' && handleAddTodo()}
        placeholder="Add a new todo"
      />
      <button onClick={handleAddTodo}>Add Todo</button>
      <TransitionGroup component="ul" style={{ listStyle: 'none', padding: 0 }}>
        {todos.map((todo, index) => (
          <CSSTransition
            key={todo + index} // Using todo + index for a unique key, though real apps might use a stable ID
            timeout={500}
            classNames="item"
            nodeRef={React.createRef<HTMLLIElement>()} // Required for Strict Mode and React 18+
          >
            {(state) => (
              <li ref={state.nodeRef} style={{marginBottom: '5px'}}>
                <TodoItem todo={todo} onRemove={() => handleRemoveTodo(index)} />
              </li>
            )}
          </CSSTransition>
        ))}
      </TransitionGroup>
    </div>
  );
};

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

view raw JSON →