React Native ActivityIndicator for a Quick & Easy Loading Animation

It’s simple, it’s cross platform, it’s quick, and it works.

Photo by NeONBRAND on Unsplash

In case you need to whip up a quick loading animation, use the React Native ActivityIndicator!

We’ll be using React Native’s ActivityIndicator and Modal to create a simple loading animation. Here is our final product (shown on an iOS simulator).

Loading during API call using React Native Activity Indicator

I’m using the Google Maps API to request the address, but it’s super fast, so I’m also using a timeout to give us more time to see the loading animation.

Presentational Component — Loader

Let’s start by creating our presentational component file. I call mine loader.js. When I’m creating presentational components, I like to immediately import them into a screen or container component also so I can see changes as I go along. The initial start to my Loader component looks something like this.

import React, { Component } from 'react';
import {
StyleSheet,
View,
Modal,
ActivityIndicator
} from 'react-native';
const Loader = props => {
const {
loading,
...attributes
} = props;
return (
<Modal
visible={loading}></Modal>
)
}
const styles = StyleSheet.create({

});
export default Loader;

React Native Modal

The container of my loader is going to be a full screen modal because I don’t want users to be able to perform any actions on the screen while my data is loading. So I can go ahead and add that as my only markup for now. In the Modal docs from React Native, you can see they give us this visible property that will show and hide as needed.

Great — so this gives us something that we can export and import from our container to get things just barely off the ground at least.

Container Component

Back in our container or screen component, let’s import what we have so far.

import React, { Component } from 'react';
import {
StyleSheet,
View,
Text,
Dimensions
} from 'react-native';
import Button from 'MyButton';
import Loader from 'Loader';

The main thing to note is the import Loader from 'Loader'; line as it relates to this instruction, but I’m including everything just so there isn’t confusion where things are coming from in my view. For my container, I’m using a screen in a sample app. I’ll be using a StyleSheet and View as usual, adding in Text and Dimensions as well. I’m also importing a custom Button component.

After we add the imports, we can define our container component class, and add loading to our state. I also have address in my state because that is what my API is going to return. Below is where we are after these updates.

...
class ActivityIndicatorScreen extends Component {
  constructor(props) {
super(props);
    this.state = {
loading: true,
address: ''
}
}
  getCoordinates(query) {
console.log('start loading animation');
}
  render() {
return (
<View style={styles.container}>
<Loader
loading={this.state.loading} />
<Text
style={{fontSize: 24, paddingBottom: 20, fontWeight: 'bold'}}>Infinity Yoga Brookhaven</Text>
<Button
containerViewStyle={{width: '100%', marginBottom: 20}}
onPress={() => this.getCoordinates('infinity yoga brookhaven')}
title="Get Address"
fontWeight="bold"
buttonStyle={{borderRadius: 2}}
backgroundColor='#333333'
underlayColor="#cccccc" />
<Text
style={{fontSize: 18, paddingBottom: 10}}>{`${this.state.address}`}</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#CCCCCC',
height: Dimensions.get('window').height,
padding: 15,
display: 'flex',
alignItems: 'flex-start',
width: '100%',
paddingTop: 50
}
});
export default ActivityIndicatorScreen;

Important pieces here in relation to the loading animation are:

  1. loading set to true in this.state
  2. <Loader> component with property loading set to this.state.loading
  3. getCoordinates function (or whatever you are calling to initiate the loading animation)

At this point, if you refresh your simulator, you’ll see this wonderful white screen (left in picture below)! Don’t worry, it’s just white because you have the modal visible immediately through your loading state and prop and default styles for the modal are full opacity and white background. If you change loading in state to false and refresh, you’ll see your container component view without the modal overlay (for me, looks like the right image below).

Left is with loading set to true, right is with loading set to false

Cool, so at least we know the loading component is imported correctly and working. Now we can make it look like we want it to. In loading.js, let’s go ahead and add the rest of the markup.

...
<Modal
transparent={true}
animationType={'none'}
visible={loading}>
</Modal>
...

To get the dark, but still transparent background of the modal, we’ll use transparent prop and set it to true. Also, I’m going to set my animationType to none on the modal (default is fade in).

Inside the modal, we’ll set up what we actually want the user to see.

...
<Modal
transparent={true}
animationType={'none'}
visible={loading}>
<View style={styles.modalBackground}>
<View style={styles.activityIndicatorWrapper}>
<ActivityIndicator
animating={loading} />
</View>
</View>
</Modal>
...

The first <View> wrapper will be the background of our modal. We need this so we can set the color and opacity to whatever we want. The next <View> will be the white background with rounded corners to our ActivityIndicator.

Of course, we need to include the styles for this markup, and we’ll end up with a finished loader.js file that looks something like this.

In the stylesheet, you can see the background uses flex box to center the inner view on the screen, and uses the background color black (#000000) with opacity at 40% (#00000040).

The ActivityIndicator wrapper background will be a 100x100 square with rounded corners and a white background. It also uses flex box to center the ActivityIndicator within it.

Now, as long as loading state is set to true in our container component, we can refresh (or take advantage of hot reload), and see the modal and indicator with styles applied.

Awesome! Of course, we don’t want this to always show, so let’s set loading back to false and refresh since we know it looks good.

Now we’re back to our screen with some kind of button or something to initiate the loading. All we need to do now is fill in that action in our container component file to get this thing to work.

...
async getCoordinates(query) {
this.setState({
loading: true
});
  let coords = await this.search(query);
  setTimeout(() => {
this.setState({
loading: false,
address: coords.results[0].formatted_address
});
}, 2500);
}
...

The button press action will kick off from this getCoordinates function above.

First thing I do when a user clicks the button is set loading to true. This will trigger the Loading component.

Next, I want to make my API call. Typically this will be in a store, or another file, but for this case, I just included it in the component and called it search() (see below).

You can see I added async to the beginning of my getCoordinates() function. The reason being, I want to await the response of my API call inside before I then set my loading state back to false, in turn, removing the loading indicator.

Typically, you will not include this setTimeout, but will instead simply call this.setState() and set loading to false after you await your API response. In this case though, the Google Maps API responds super fast, so in order to see my loading indicator, I need to set a timeout.

Here is that API call I mentioned earlier.

async search(query) {
let encodedAddress = encodeURIComponent(query);
  let url = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodedAddress}`;
  try{
let response = await fetch(url);
if(response.status > 400){
return {};
} else {
return await response.json();
}
} catch(e) {
return {};
}
}

So all together, I end up with a container component file looking like this.

And that’s it! To show it’s cross platform wonderfulness, here it is in Android (after not looking at Android once during development). The animation looks a little slow due to the quality of the gif and because it is running on a simulator, but you get the idea.

Android loading animation using ActivityIndicator

Hopefully this was helpful if you were looking for a simple loading animation without creating any animations on your own.


Thanks for reading! Please feel free to comment if you have any thoughts or recommendations.

If you enjoyed, go ahead and add some claps!

Happy React Native developing all. ❤