React Loading Screen Tactics — Improving User Experience
If your application gives the user a good feeling, they are more likely to continue using it.
Loading screens are a good place to start our journey of improving the feel of our applications. In the example above we are fetching data from three separate API’s before displaying the welcome screen. In the “before” loading screen, we are leaving the user in the dark. Whatever processes are running in the background are completely hidden from the user. In the “after” loading screen, we show the user what’s going on behind the scenes, then give a positive confirmation when each process finishes. The user feels they are part of the process, and it feels good.
Let’s see how we can spice up our loading screens, step by step.
Create a new app
npx create-react-app reactuxmedium
cd reactuxmedium
Install packages for animations & fading
npm i react-loading react-lottie react-fade-in bootstrap
Start app
npm start
Build simple loading screen
Head over to src/App.js
and delete everything in the <header></header> tag and replace the contents with a <Loading /> component. Then import the Loading
module from ./loading.js
(we will create this in the next step):
import Loading from "./loading.js"
src/App.js
should now look like this:
Now create a new file in the src
folder and name it loading.js
Let’s import the following (bootstrap and a simple react loader):
import React from "react";import FadeIn from "react-fade-in";import Lottie from "react-lottie";import ReactLoading from "react-loading";import "bootstrap/dist/css/bootstrap.css";
Now we can create our Loading
component:
export default class Loading extends React.Component {
constructor(props){
super(props)
this.state = {
done: undefined
}
}
render(){
return(
<h1>hello world</h1>
)
}
}
Now let’s replace the <h1></h1> with a react loader if this.state.done is false:
render() {
return (
<div>
{!this.state.done ? (
<ReactLoading type={"bars"} color={"white"} />
) : (
<h1>hello world</h1>
)}
</div>
)
}
Now we need to make an API call and set the state.done to true once it has finished:
componentDidMount() {setTimeout(() => {fetch("https://jsonplaceholder.typicode.com/posts").then(response => response.json()).then(json => this.setState({ done: true }));}, 1200);}render() {
...
We have built a simple loading screen! This is how loading.js
should look:
Improve the loading screen
There are several improvements we will make to our loading screen.
- Tell the user which API call we are currently making.
- Give a confirmation animation when the API call is complete.
- Use FadeIn to create smooth transitions.
For more flexible animation choices, we are using react-lottie. You can search for your favorite animation to use at LottieFiles. Let’s replace our current react loader with a lottie file animation. Go ahead and download the lego loader JSON here (or choose one you like), Then place the JSON file in your src
folder and rename it legoloading.json
.
Now we import the JSON in loading.js
:
import * as legoData from "./legoloading.json";
Initialize the options for our animation:
...
import * as legoData from "./legoloading.json"const defaultOptions = {loop: true,autoplay: true,animationData: legoData.default,rendererSettings: {preserveAspectRatio: "xMidYMid slice"}}export default class Loading extends React.Component {
...
Now replace
<ReactLoading type={"bars" color={"white"} />
With
<FadeIn>
<div class="d-flex justify-content-center align-items-center">
<h1>fetching pizza</h1>
<Lottie options={defaultOptions} height={120} width={120} />
</div>
</FadeIn>
The last step is to switch the lego animation to a success animation when this.state.done is true. But wait, we already have an action on this.state.done, we are switching to “hello world” when the api call is complete. This means we will not be able to see the success animation. To fix this we will create a separate this.state.loading for when the API call is complete, then set a short timeout before activating this.state.done:
this.state = {
loading: undefined,
done: undefined
};
And change our componentDidMount function:
componentDidMount() {
setTimeout(() => {
fetch("https://jsonplaceholder.typicode.com/posts")
.then(response => response.json())
.then(json => {
this.setState({ loading: true });
setTimeout(() => {
this.setState({ done: true });
}, 1000);
});
}, 1200);
}
Perfect, now we can switch out our success animation. Download the lottie file here, place it in src
directory, and rename it to doneloading.json
Now import it as doneData:
import * as doneData from "./doneloading.json";
And create a defaultOptions2, notice that for this animation we set loop to false:
const defaultOptions2 = {
loop: false,
autoplay: true,
animationData: doneData.default,
rendererSettings: {
preserveAspectRatio: "xMidYMid slice"
}
};
Now we can add the success animation when this.state.loading is true:
<FadeIn>
<div class="d-flex justify-content-center align-items-center
<h1>fetching pizza</h1>
{!this.state.loading ? (
<Lottie options={defaultOptions} height={120} width={120} /
) : (
<Lottie options={defaultOptions2} height={120} width={120} />
)}
</div>
</FadeIn>
This is how our completed loading.js
should look: