# react-transition-group: the exit animation half of React that never shipped

> A set of primitives for mounting and unmounting components over time, last released in 2022 and still widely installed, with the real documentation living off the repository.

**reactjs/react-transition-group** — An easy way to perform animations when a React component enters or leaves the DOM

- Repository: https://github.com/reactjs/react-transition-group
- Website: https://reactcommunity.org/react-transition-group/
- Stars: 10,211 · Forks: 637
- Language: JavaScript
- License: NOASSERTION
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/reactjs-react-transition-group

## Managing mount and unmount over time, not animating

The repository description says it all in one line: an easy way to perform animations when a React component enters or leaves the DOM. Read that carefully, because the library's actual job is narrower than the word animations suggests. React removes an unmounting component from the tree immediately, which gives a CSS transition no chance to play. This library inserts a state machine in between, so a component can exist in an exiting state for as long as your transition needs, and only then does React drop it.

The README's own description is more precise about the mechanism: a set of components for managing component states, including mounting and unmounting, over time, specifically designed with animation in mind. States, not pixels. The library hands you `entering`, `entered`, `exiting` and `exited`, and what happens visually is entirely up to you.

That division is the design, and it is why the package has held up. It does not ship a CSS engine, a keyframe system or a duration model. It does not care whether your transition is 200 milliseconds of opacity or a Web Animations API call in a callback. It cares that the component was still in the tree when you needed it to be.

The components you'd reach for are Transition, CSSTransition and TransitionGroup, with ReplaceTransition for swaps. Transition handles a single element, TransitionGroup handles a keyed list, and CSSTransition is the adapter that adds and removes class names on a fixed schedule.

## The v1 and v2 split is the first thing any reader should know

The README opens with a warning block rather than a description, and the warning is about history. The API in v2 and above is not backwards compatible with the original React addon, the v1-stable line. The README says to use the v1 release as a drop-in replacement for `react-addons-transition-group` and `react-addons-css-transition-group`, and points to the v1-stable branch for its documentation and code.

It then says the v1 codebase is no longer being updated and to upgrade to the latest version when possible. That combination is the awkward state this project has been in for years: an unmaintained compatibility path kept alive for people who cannot yet move.

If you are porting old code, the migration guide lives in the repository at `Migration.md` rather than on the documentation site, which is one of the few places where this README sends you inward instead of outward. The tree confirms it: `Migration.md` sits beside `README.md`, `CHANGELOG.md` and `LICENSE`.

The other historical detail worth carrying is the TypeScript situation. Definitions are not bundled in this package. They live in DefinitelyTyped and are installed separately, which is the sort of thing that silently fails to type a `Transition` prop if nobody added the types package:

```
npm install @types/react-transition-group
```

This is normal for older libraries and mildly annoying for everyone still using them.

## GitHub reports no license, the package manifest says BSD-3-Clause

This repository has a licensing inconsistency worth naming rather than smoothing over. GitHub reports the license as NOASSERTION, its way of saying the automated classifier could not determine the terms. The tree does contain a `LICENSE` file, and the package manifest is unambiguous: the `license` field reads BSD-3-Clause.

Both facts are on the record and neither is necessarily wrong. A classifier can fail on a LICENSE file it does not recognise while the declared SPDX identifier in package.json remains correct, and BSD-3-Clause is what npm and every downstream tool will read anyway. The practical reading is that the intended license is BSD-3-Clause as declared in the manifest, and the repository metadata is simply uninformed.

For a package that has been in the React ecosystem for years and shows up in dependency trees of large applications, the manifest field is the one that matters, since that is what package managers and license scanners consume. If your organisation's process requires reading the license text itself, the `LICENSE` file in the tree is there to read.

This is a good example of why repository metadata fields should be treated as hints rather than as authoritative. The same caution applies to the homepage field, which points at a documentation site maintained by the React community rather than at anything under the reactjs organisation.

## Last release 2022, last push 2026, and what the patch notes fixed

The release history is three entries long in what is visible here, all clustered in a single week in 2022. v4.4.3 landed on 2022-07-30 and fixed enter animations with mountOnEnter or unmountOnExit. v4.4.4 followed the same day and fixed missing build files. v4.4.5 landed on 2022-08-01 and applied the entering animation synchronously when unmountOnExit or mountOnEnter is enabled.

Read those three notes as one bug. A component configured to mount on enter or unmount on exit was not starting its enter animation, and the 4.4.5 fix makes the animation apply synchronously in those cases. That is the mount-on-enter and unmount-on-exit path, which is exactly the path people use when they want the element absent from the DOM until it appears.

The package manifest still reads version 4.4.5, so the build published under that tag is the one npm installs. The repository's last recorded push is dated 2026-03-05, which is later than the last release by a long margin. Commits without a release usually mean dependency, tooling or CI work rather than user-facing changes, and the package has accumulated 258 open issues, which is a large queue for a package of this size.

The tree gives a good picture of the project's structure: `src/` for the components, `test/` for the suite, `stories/` for Storybook examples, and `www/` which is a separate package holding the documentation site. The scripts confirm it, since `bootstrap`, `build:docs`, `start` and `deploy-docs` all delegate to `yarn --cwd www`.

## Running the examples means running Storybook

The README's Examples section is four lines and it tells you to clone the repository first, which is a good sign about what this project is for. There is no CodeSandbox or hosted demo. Instead it points at the stories directory in this repo and tells you to run npm install, or yarn, and then `npm run storybook`.

That workflow suits a library whose examples are the specification. When a component's contract is a set of state transitions, a live Storybook where you can toggle props and watch the timing is a better reference than static documentation, and the repository has a dedicated `stories/` directory to hold exactly that.

The scripts give you a fuller picture of how the project builds. `test` runs lint and then jest. `build` clears the lib directory and produces CommonJS, ESM, a cherry-picked build and a rollup bundle, then copies the README and LICENSE into lib. `tdd` is jest in watch mode. `lint` runs eslint and prettier in parallel through npm-run-all.

```json
"test": "npm run lint && npm run testonly",
"testonly": "jest --verbose",
"tdd": "jest --watch",
```

Releases go through semantic-release, with a `release:next` script that tags a beta preid. The `.size-snapshot.json` file in the tree is the kind of artifact a project commits when it cares about bundle size, which for an animation library that most applications will bundle is a reasonable thing to track.

## Conclusion

react-transition-group solves a narrow problem cleanly: giving a component an exit phase so it can animate away before React removes it from the tree. It does not animate anything itself, it coordinates state transitions and leaves the actual visual work to CSS classes or your own callbacks, and it still depends on ReactDOM.findDOMNode, which is why it has not been reworked for concurrent rendering. The README is a signpost to reactcommunity.org rather than a manual, so the component reference lives elsewhere and only the migration and TypeScript notes live here. For a new project, reach for it when you need controllable enter and exit phases and nothing more; the documentation site, not this repository, is where you should confirm the behaviour of the state you intend to use.

## FAQ

### How do I make a React component animate out before it unmounts?

Wrap the element in Transition and give it a timeout. The component stays mounted in an exiting state until the timeout elapses, which is what gives a CSS transition the time it needs. TransitionGroup does the same job across a keyed list, and CSSTransition adds and removes class names on that schedule.

### What is the difference between Transition and CSSTransition?

Transition only manages the lifecycle states such as entering, entered, exiting and exited. CSSTransition sits on top of it and applies class names on a fixed schedule with the usual nodeRef, classNames and timeout props, so you write CSS rather than callbacks. Neither one animates anything by itself.

### Does react-transition-group ship its own TypeScript types?

No. The definitions are maintained in DefinitelyTyped and installed separately with npm install @types/react-transition-group. If prop types are missing in your editor, that package is the reason.

### What license is react-transition-group released under?

The package manifest declares BSD-3-Clause, and a LICENSE file is present in the repository, though GitHub's own classifier reports no recognised license. Package managers and license scanners read the manifest field, so BSD-3-Clause is the term that applies in practice.

## Sources

- [Issues](https://github.com/reactjs/react-transition-group/issues)
- [Project website](https://reactcommunity.org/react-transition-group/)
- [reactjs/react-transition-group on GitHub](https://github.com/reactjs/react-transition-group)
- [README](https://github.com/reactjs/react-transition-group/blob/master/README.md)
- [Releases](https://github.com/reactjs/react-transition-group/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/reactjs-react-transition-group
