Skip to content

Modus-Logo-Long-BlackCreated with Sketch.

  • Services
  • Work
  • Blog
  • Resources

    OUR RESOURCES

    Innovation Podcast

    Explore transformative innovation with industry leaders.

    Guides & Playbooks

    Implement leading digital innovation with our strategic guides.

    Practical guide to building an effective AI strategy
  • Who we are

    Our story

    Learn about our values, vision, and commitment to client success.

    Open Source

    Discover how we contribute to and benefit from the global open source ecosystem.

    Careers

    Join our dynamic team and shape the future of digital transformation.

    How we built our unique culture
  • Let's talk
  • EN
  • FR

Fluid animations improve the user experience of any application. React Native is focused on performance to build and deliver great products.


NEW RESEARCH: LEARN HOW DECISION-MAKERS ARE PRIORITIZING DIGITAL INITIATIVES IN 2024.

Get Report


In this tutorial we will learn the basics of animations. We are going to create a panel component. When the body of the component expands or collapses, we will add a nice animation as shown in the next image.

Panel Component with a Nice Animation

Let’s start by creating our project. If this is your first time with React Native please follow the Getting Started guide on the official website, then run the following command on your terminal.

$ react-native init Panels

Once the previous command returns, we can open ios/Panels.xcodeproj in XCode. Now we can run the application on the iOS simulator. We should see something like in the following image.

Panel Component displaying Welcome to React Native

We have our app running correctly, now we are ready to start coding!

Creating the panel component

Let’s start by creating the panel component. For now we will only have the title and the content. Create a components folder on the root of the project, we will have all our JavaScript classes in here. Now, let’s create a Panel.js file and add the following code.

import React,{Component} from 'react'; //Step 1
import {StyleSheet,Text,View,Image,TouchableHighlight,Animated} from 'react-native';

class Panel extends Component{
    constructor(props){
        super(props);

        this.icons = {     //Step 2
            'up'    : require('./images/Arrowhead-01-128.png'),
            'down'  : require('./images/Arrowhead-Down-01-128.png')
        };

        this.state = {       //Step 3
            title       : props.title,
            expanded    : true
        };
    }

    toggle(){
        
    }


    render(){
        let icon = this.icons['down'];

        if(this.state.expanded){
            icon = this.icons['up'];   //Step 4
        }

        //Step 5
        return ( 
            <View style={styles.container} >
                <View style={styles.titleContainer}>
                    <Text style={styles.title}>{this.state.title}</Text>
                    <TouchableHighlight 
                        style={styles.button} 
                        onPress={this.toggle.bind(this)}
                        underlayColor="#f1f1f1">
                        <Image
                            style={styles.buttonImage}
                            source={icon}
                        ></Image>
                    </TouchableHighlight>
                </View>
                
                <View style={styles.body}>
                    {this.props.children}
                </View>

            </View>
        );
    }
}
export default Panel;
  1. We just import all the dependencies that we are using in our Panel class.
  2. Then we load two images. We will use them in the collapsible/expandible button that we will add in the title bar.
  3. We set the initial state of our app. Here we are getting the title and setting the expanded property as true.
  4. Based on the expanded property, we get the correct image to render on the button.
  5. Finally, we render the components. Basically a View with a title and a content. For the body we are using this.props.children, this means that we will render any component on the body.

Now we need to add some styles, something really basic to keep things simple.

var styles = StyleSheet.create({
    container   : {
        backgroundColor: '#fff',
        margin:10,
        overflow:'hidden'
    },
    titleContainer : {
        flexDirection: 'row'
    },
    title       : {
        flex    : 1,
        padding : 10,
        color   :'#2a2f43',
        fontWeight:'bold'
    },
    button      : {

    },
    buttonImage : {
        width   : 30,
        height  : 25
    },
    body        : {
        padding     : 10,
        paddingTop  : 0
    }
});

We are just adding some colors, dimensions and paddings. The code is self explanatory because the properties are very similar to CSS.

Now that we have our Panel component ready, let’s create some instances in our app. Open the index.ios.js file and add the following code.

import React, {Component} from 'react';
import {AppRegistry,StyleSheet,Text,ScrollView} from 'react-native';
import Panel from './components/Panel';  // Step 1

class Panels extends Component {
  render() {
    return (  //Step 2
      <ScrollView style={styles.container}>
        <Panel title="A Panel with short content text">
          <Text>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</Text>
        </Panel>
        <Panel title="A Panel with long content text">
          <Text>Lorem ipsum...</Text>
        </Panel>
        <Panel title="Another Panel">
          <Text>Lorem ipsum dolor sit amet...</Text>
        </Panel>
      </ScrollView>
    );
  }
};

var styles = StyleSheet.create({
  container: {
    flex            : 1,
    backgroundColor : '#f4f7f9',
    paddingTop      : 30
  },
  
});

AppRegistry.registerComponent('Panels', () => Panels);
  1. First we need to include our new component.
  2. Next we render our component three times with a title and some content. The content could be anything, but we will use text for the purposes of this demonstration.

Remember to restart the packager in order to get the images working, otherwise you will get an error when loading the images. Once the packager is restarted, we should see something as in the following image.

Restarted Packager with multiple panel settings displayed with sample text

Running the animations

Now that we have our component ready we can add the animations. When the user taps on the expand/collapse button, we are going to change the height of the main container with a nice animation.

The first thing we need to do is to create an instance of the Animated.Value class and pass the initial value 0. This class is responsible for holding the value of each frame during the animation. In our case, it will hold the value of the height. Let’s add the following code in the constructor of the Panel class.

this.state = {
    title       : props.title,
    expanded    : true,
    animation   : new Animated.Value(0),
    minHeight   : 0,
    maxHeight   : 0,
    maxValueSet : false,
    minValueSet : false,
    cardHeight  : 'auto'

};

Now we have the animation property in the component state. We can use the same instance in several animations, for example to animate the opacity, the width, scale, almost any style property. However, this instance can only be used by one animation at the time.

Now we need to get the maximum and minimum height of our panel on the first load of application. We need to dynamically calculate this, because as mentioned before, there could be anything in the content. To get those dimensions, we can use the onLayout event of the View component. This event will be fired after the component is rendered and all sizes are calculated based on the styles and the content.

Let’s add those listeners inside of the render method.

render(){
    //...

    return (
        <View style={styles.container}>
            <View style={styles.titleContainer} onLayout={this._setMinHeight.bind(this)}> //Step 1
                //...
            </View>
            
            <View style={styles.body} onLayout={this._setMaxHeight.bind(this)}> //Step 2
                {this.props.children}
            </View>

        </View>
    );
}
  1. First we add the onLayout listener to the title view. The _setMinHeight method will be called when the title gets rendered. To execute this method only once, we are checking the maxValueSet variable value to be false, after it runs we set its value to be true. So this method shouldn’t run again. Along with minHeight we also set the initial value of animation.
  2. Then we add the onLayout listener to the body view. In this case the _setMaxHeight method will be executed when the body gets rendered. To execute this method only once, we are checking the minValueSet variable value and this should be false, after it runs we set its value to be true. So this method shouldn’t run again.

Now we need to define the callback methods in our Panel class.

_setMaxHeight(event){
if(!this.state.maxValueSet) {
    this.setState({
        maxHeight   : event.nativeEvent.layout.height,
	maxValueSet : true
    });
}
}

_setMinHeight(event){
if(!this.state.minValueSet) {
	this.state.animation.setValue(event.nativeEvent.layout.height);
    this.setState({
        minHeight   : event.nativeEvent.layout.height,
	minValueSet : true
    });
}
}

All we are doing here is getting the height and creating a new state property. We are going to use these values inside the toggle method. Basically to set the limits of the animation.

Now that we have the limit values, we can calculate the animation values. Let’s add the following code inside the toggle method.

toggle(){
    //Step 1
    let initialValue    = this.state.expanded? this.state.maxHeight + this.state.minHeight : this.state.minHeight,
        finalValue      = this.state.expanded? this.state.minHeight : this.state.maxHeight + this.state.minHeight;

    this.setState((prevState) => {
        expanded : !prevState.expanded  //Step 2
    });

    this.state.animation.setValue(initialValue);  //Step 3 (Remove this line)
    Animated.spring(     //Step 4
        this.state.animation,
        {
            toValue: finalValue,
		useNativeDriver: true
        }
    ).start();  //Step 5
}
  1. We set the initial and final value, in here we are using the limits from the previous steps. If the component is expanded we set the height to the minimal value, otherwise to the maximum value.
  2. We need to toggle the expanded value.
  3. Set useNativeDriver: true. So, JS Native drive runs animation into a separate UI thread in place of JS thread, and JS thread is available to run complex programmatic logic
  4. Using the Animated.Value instance, we set the initial value for this animation.
  5. We use the Animated.spring method to run the animation. This method does all the calculations and set the value for each frame to the Animated.Value instance we declared in the constructor. We are also setting the end value of the animation, by using an object as a second parameter.
  6. We call the start method to run all the calculations.

If we go and try our code in the simulator, we will see that only the image is changing. But nothing else is happening.

Attach listener on height change event and assign its value to cardHeight. Also unregister that listener on unload components.

componentDidMount() {
       this.animationId = this.state.animation.addListener(({value}) => {
           this.setState({
               cardHeight: value
           });
       });
   }

   componentWillUnmount() {
       this.state.animation.removeListener(this.animationId);
   }

The last piece of the puzzle, is to actually set the Animated.Value instance to the component that we want to animate. Right now we have all the calculations and everything, but we haven’t assigned those values to the height of the component that we want to animate.

Let’s modify the render method as follows:

render(){
    //...

    return (
        <Animated.View 
            style={[styles.container,<span style="font-weight: 400;">{</span><span style="font-weight: 400;">height</span><span style="font-weight: 400;">:</span> <span style="font-weight: 400;">this</span><span style="font-weight: 400;">.</span><span style="font-weight: 400;">state</span><span style="font-weight: 400;">.</span><span style="font-weight: 400;">cardHeight</span><span style="font-weight: 400;">}]}</span>>
            
//...

        </Animated.View>
    );
}

The first thing we need to do is to use an Animated.View instead of a simple View as the main container. Now we need to select the style property that we want to animate, in this case we want to animate the height, but we can also animate the opacity, or the width, or any other style property.

The height property receives the Animated.Value instance, this is the same instance we are using in the Animated.spring method. This will set all the calculated values to the height property, and we will be able to see a nice animation when collapsing and expanding the body.

Restarted Packager with multiple panel settings displayed with sample text with animation

Conclusion

There are only three concepts that we need to know when dealing with animations. First we need the Animated.Value to hold the value for each frame during the animation. Second, we need to calculate the values for each animation using the Animated.spring method (There are 2 other methods that I might cover on future tutorials). Third, we need to use an Animated.View component instead of a regular View and assign the calculated values to the style property that we want to animate.

Small simple animations will improve the user experience of your application. We can create many other complex animations, but the concepts are the same. If you have any questions please leave a comment or ping me on Twitter.

Need efficient and scalable software solutions? Learn more about our software development expertise.

This blog was updated on May 2, 2022 by Akhilesh Jain, Full-Stack Engineer at Modus Create. 

Posted in Application Development
Share this

Crysfel Villa

Crysfel Villa was a Sr. Engineer at Modus Create. He's a passionate JavaScript coder and an accomplished software developer with over 8 years of experience on technical training, consulting and systems analysis. When he's away from the keyboard, he enjoys playing the guitar, piano and violin. Crysfel currently lives in NY and can be found attending tech meetups throughout the city. He has co-authored The React Native book, which was published in December 2016.
Follow

Related Posts

  • React Navigation and Redux in React Native Applications
    React Navigation and Redux in React Native Applications

    In React Native, the question of “how am I going to navigate from one screen…

  • Using ES2016 Decorators in React Native
    Using ES2016 Decorators in React Native

    *picture courtesy of pixabayDecorators are a popular bit of functionality currently in Stage 1 of…

Want more insights to fuel your innovation efforts?

Sign up to receive our monthly newsletter and exclusive content about digital transformation and product development.

What we do

Our services
AI and data
Product development
Design and UX
Modernization
Platform and MLOps
Developer experience
Security

Our partners
Atlassian
AWS
GitHub
Other partners

Who we are

Our story
Careers
Open source

Our work

Our case studies

Our resources

Blog
Innovation podcast
Guides & playbooks

Connect with us

Get monthly insights on AI adoption

© 2025 Modus Create, LLC

Privacy PolicySitemap
Scroll To Top
  • Services
  • Work
  • Blog
  • Resources
    • Innovation Podcast
    • Guides & Playbooks
  • Who we are
    • Our story
    • Careers
  • Let’s talk
  • EN
  • FR