A React component to animate replacing one element with another.
MIT License
A React component to animate replacing one element with another.
While ReactCSSTransitionGroup
does a great job
of animating changes to a list of components and can even be used to animate the replacement of one item
with another, proper handling of the container height in the latter case is not built in. This component
is designed to do exactly that with an API closely following that of ReactCSSTransitionGroup
.
Using react-css-transition-replace
provides two distinct benefits:
Animations are fully configurable with CSS, including having the entering component wait to enter until the leaving component's animation completes. Following suit with the React.js API the one caveat is that the transition duration must be specified in JavaScript as well as CSS.
Live Examples | Change Log | Upgrade Guide
Install via npm
:
npm install --save react-css-transition-replace
All functional child components must be wrapped in forwardRef
to work correctly.
A ReactCSSTransitionReplace
component can only have a single child. Other than that, the basic usage
follows the exact same API as ReactCSSTransitionGroup
, with support for transitionEnter
, transitionLeave
and transitionAppear
. When the key
of the child component changes, the previous component is animated out
and the new component animated in. During this process:
absolute
positioning.transitionName
is a String
the {animation-name}-height
class name is appliedtransitionName
is an Object
the transitionName.height
class will be used if present.isLeaving
prop while transitioning out.This provides many possibilities for animating the replacement as illustrated in the examples below.
Additionally, the boolean property changeWidth
can be used to animate changing the width of the component.
This change will happen at the same time as changing the height. Animating this change should be done using
the same class name that is used for animating the change in height.
It is also possible to remove the child component (i.e. leave ReactCSSTransitionReplace
with no children)
which will animate the height
going to zero along with the leave
transition. Similarly, a single child
can be added to an empty ReactCSSTransitionReplace
, triggering the inverse animation.
By default a span
is rendered as a wrapper of the child components. Each child is also wrapped in a span
used in the positioning of the actual rendered child. These can be overridden with the component
and
childComponent
props respectively.
The ReactCSSTransitionReplace
component is used exactly like its ReactCSSTransitionGroup
counterpart:
import ReactCSSTransitionReplace from 'react-css-transition-replace';
render() {
return (
<ReactCSSTransitionReplace transitionName="cross-fade"
transitionEnterTimeout={1000} transitionLeaveTimeout={1000}>
<SomeComponent key="uniqueValue"/>
</ReactCSSTransitionReplace>
);
}
To realize cross-fading of two components all that remains is to define the enter and leave opacity transitions in the associated CSS classes:
.cross-fade-leave {
opacity: 1;
}
.cross-fade-leave.cross-fade-leave-active {
opacity: 0;
transition: opacity 1s ease-in;
}
.cross-fade-enter {
opacity: 0;
}
.cross-fade-enter.cross-fade-enter-active {
opacity: 1;
transition: opacity 1s ease-in;
}
.cross-fade-height {
transition: height 0.5s ease-in-out;
}
Note the additional .cross-fade-height
class. This indicates how the container height is to be
animated if the heights of the entering and leaving components are not the same. You can see this
in action here.
To fade a component out and wait for its transition to complete before fading in the next, simply
add a delay to the enter
transition.
.fade-wait-leave {
opacity: 1;
}
.fade-wait-leave.fade-wait-leave-active {
opacity: 0;
transition: opacity 0.4s ease-in;
}
.fade-wait-enter {
opacity: 0;
}
.fade-wait-enter.fade-wait-enter-active {
opacity: 1;
/* Delay the enter animation until the leave completes */
transition: opacity 0.4s ease-in 0.6s;
}
.fade-wait-height {
transition: height 0.6s ease-in-out;
}
Note: The transitionEnterTimeout
specified in the JS must be long enough to allow for the delay and
the duration of the transition. In this case:
<ReactCSSTransitionReplace transitionName="fade-wait"
transitionEnterTimeout={1000} transitionLeaveTimeout={400}>
See the live example here.
Animated transitions of react-router v4 routes is supported with two caveats shown in the example below:
location
must be applied to the Switch
to force it to maintain the previous matched route onSwitch
might render null
, i.e. there is no catch-all "*"
route, the Switch
must be wrapped in adiv
or similar for the leave transition to work; if not the previous component will disappear instantaneously<Router>
<div className="router-example">
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/one">One</Link>
</li>
<li>
<Link to="/two">Two</Link>
</li>
<li>
<Link to="/three">Three (no match)</Link>
</li>
</ul>
<Route
render={({ location }) => (
<ReactCSSTransitionReplace
transitionName="fade"
transitionEnterTimeout={500}
transitionLeaveTimeout={500}
>
<div key={location.pathname}>
<Switch location={location}>
<Route path="/" exact component={Home} />
<Route path="/one" component={One} />
<Route path="/two" component={Two} />
</Switch>
</div>
</ReactCSSTransitionReplace>
)}
/>
</div>
</Router>
See the live example here.
For smoother transitions hardware acceleration, which is achieved by using translate3d instead of the 2D translations, should be used whenever possible. For example, to realize a mobile app transition between pages one might use:
.page-enter,
.page-leave {
position: absolute;
-webkit-transition: transform 250ms ease-in-out, opacity 250ms ease-in-out;
transition: transform 250ms ease-in-out, opacity 250ms ease-in-out;
}
.page-enter {
left: 100vw;
}
.page-enter.page-enter-active {
-webkit-transform: translate3d(-100vw, 0, 0);
transform: translate3d(-100vw, 0, 0);
}
.page-leave {
left: 0;
}
.page-leave.page-leave-active {
-webkit-transform: translate3d(-100vw, 0, 0);
transform: translate3d(-100vw, 0, 0);
}
<ReactCSSTransitionReplace
transitionName="page"
transitionEnterTimeout={250}
transitionLeaveTimeout={250}
>
<div key="page01">My page 01 content</div>
</ReactCSSTransitionReplace>
block
or inline-block
level elements is more stable that inline
elements. If thespan
or other inline element used as the outeroverflow
of the container is set to 'hidden'
automatically, which changes the behaviour of'visible'
. This may cause a glitch in the height at the start or end of animations. To avoid this you can:
overflowHidden={false}
prop when hidden overflow is not needed,.*-height
class (or transitionName.height
) is not specified the change in container height will notPRs are welcome.
This software is free to use under the MIT license. See the LICENSE file for license text and copyright information.