Animating appearance & disappearance in React Native
24 Apr 2017
Things don't just appear on the phone screen out of the blue. Showing the user how the UI elements fit together, where they come from, and where they go, is the cornerstone of a great user experience.
You may already know that animating the appearance of something can be straightforward. But what about when something needs to be hidden? If the component is already unmounted, how do you even animate its disappearance?
Seems... impossible, right?
But then, how does everyone have that? And not just everyone, but how can you make something like this?
How components are shown conditionally
Typically, to have a component either shown or hidden, depending on some state variable, we use expressions like this:
{
showRectView && <RectView />;
}
But we don't want this component just to appear, we want to fade it in.
First shot at animating
Now that you want to somehow animate its appearance and disappearance, you could create a Fade
component that would run the appearance animation, and use it like this:
{
showRectView && (
<Fade>
<RectView />
</Fade>
);
}
The thing is, it would not animate on unmount — because once showRectView
is false
, Fade
will be unmounted too, and will not have a chance to animate the view out.
What we want is the way to keep rendering RectView
until the animation has finished.
And the Fade
component is the best place to handle that.
A different approach
Instead of conditionally rendering, we can pass a visible=true/false
prop to Fade
, and let it animate its children on both appearance and disappearance.
The Fade
component itself can be rendered at all times:
<Fade visible={showRectView}>
<RectView />
</Fade>
Fade
implementation
The simplest Fade
could be implemented using these building blocks of React Native animations:
- Need for change: we want its children to change opacity from 0 to 1, and scale from 1.1 to 1.0 when appearing; and the other way around when disappearing.
- Visual state: our visual state will be the visibility of its children, which runs from 0 (hidden) to 1 (visible).
- Transitions: this can be anything. We can use the simplest one,
timing
. - The pixels: we will map the 0-1 visibility value into opacity between 0 and 1, and transform's scale between 1.1 and 1.0.
Want a handy little PDF to help you recall the building blocks? You can download the cheatsheet below. Otherwise keep reading.

Exclusive bonus: Download the free Building blocks of React Native animations cheatsheet that you can print out or have handy on your desktop.
I'll email it to you right away so you can access it from anywhere!No spam. I will occasionally send you my posts about JavaScript and React.
Article continues:
import { Animated } from "react-native";
class Fade extends Component {
componentWillMount() {
this._visibility = new Animated.Value(this.props.visible ? 1 : 0);
}
componentWillReceiveProps(nextProps) {
Animated.timing(this._visibility, {
toValue: nextProps.visible ? 1 : 0,
duration: 300,
});
}
render() {
const { visible, style, children, ...rest } = this.props;
const containerStyle = {
opacity: this._visibility.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
}),
transform: [
{
scale: this._visibility.interpolate({
inputRange: [0, 1],
outputRange: [1.1, 1],
}),
},
],
};
const combinedStyle = [containerStyle, style];
return (
<Animated.View style={combinedStyle} {...rest}>
{children}
</Animated.View>
);
}
}
Which is awesome already!
The thing is, it keeps rendering children
with opacity = 0
even when visible=false
.
But we don't want that, of course.
One idea might be to render anything when this.props.visible
is true
, but this still has the drawbacks of the conditional rendering solution.
Instead, we need to be able to unmount the children after the animation has finished running.
To get there, we can keep a state property, called, quite uncreatively, visible
.
We will change it as follows:
- when the
visible
prop becomestrue
, set thevisible
in the state totrue
immediately. - when the
visible
prop becomesfalse
, set thevisible
in the state tofalse
after the animation has finished.
constructor(props) {
super(props);
this.state = {
visible: props.visible,
};
};
componentWillReceiveProps(nextProps) {
if (nextProps.visible) {
this.setState({ visible: true });
}
Animated.timing(this._visibility, {
toValue: nextProps.visible ? 1 : 0,
duration: 300,
}).start(() => {
this.setState({ visible: nextProps.visible });
});
}
And now, you can get a nice appearance and disappearance animation by wrapping a component into:
// determine whether to show, possible based on state
const visible = ...
<Fade visible={visible}>
<SomeComponent />
</Fade>
The full source of the Fade
component would be:
import Animated from "react-native";
class Fade extends Component {
constructor(props) {
super(props);
this.state = {
visible: props.visible,
};
}
componentWillMount() {
this._visibility = new Animated.Value(this.props.visible ? 1 : 0);
}
componentWillReceiveProps(nextProps) {
if (nextProps.visible) {
this.setState({ visible: true });
}
Animated.timing(this._visibility, {
toValue: nextProps.visible ? 1 : 0,
duration: 300,
}).start(() => {
this.setState({ visible: nextProps.visible });
});
}
render() {
const { visible, style, children, ...rest } = this.props;
const containerStyle = {
opacity: this._visibility.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
}),
transform: [
{
scale: this._visibility.interpolate({
inputRange: [0, 1],
outputRange: [1.1, 1],
}),
},
],
};
const combinedStyle = [containerStyle, style];
return (
<Animated.View style={this.state.visible ? combinedStyle : containerStyle} {...rest}>
{this.state.visible ? children : null}
</Animated.View>
);
}
}