How To Use Image Placeholders in React Native

JavaScript
How to Use Image Placeholders in React Native

I’ve been working with React Native for over a year now. And one thing that is driven home by my UX team is the need to provide feedback to the user that something is happening. What I’ve found is that while the React Native <Image/> component provides us with the ability to have a placeholder via the defaultSource property, this can lead to a less than optimal user experience if not implemented properly.

Whether it’s within a ListView implementation or what is known as a Detail View, rendering images within mobile applications requires some finesse, given the UX demands of finely developed mobile applications. Web developers transitioning to React Native need to pay particular attention to how they render images in their application, where the “web way” of rendering images just won’t fly. For example, images rendering as they download is not something that is good UX for mobile applications.

Below is a gif of the YouTube homepage on a desktop loading on a slow network.

How to Use Image Placeholders in React Native 1

We can see that there are grey boxes as placeholders in the images above. We’re going to replicate that behavior in this post, as we take a deep dive into a pattern that you can leverage to easily add a visually pleasing pre-loading placeholder to your application’s Images.

What we’re building

We’ll create a very simple application that displays a two-column grid of Images. We’ll be using a plain <ScrollView/> over a <ListView/> component because a <ListView/> would simply make things more complicated than necessary to demonstrate this pattern, though it is advised to use a <ListView/> for this pattern for optimal performance. The goal of this post is to drive home the need for a multi-phase approach for rendering with placeholder images.

How to Use Image Placeholders in React Native 2

As you’ll soon learn, the code for this application is relatively simple. But in case you’re new to using React JS and React Native, I’ll explain all of it.

If you’re like me, you might want to see this running right away. I’ve setup a github repo here.

If you’re new to the React Native ecosystem, it might be prudent to review the code structure first. Otherwise, feel free to jump to the next section.

Contents of index.ios.js

import React, {
    // All necessary imports
} from 'react-native';

// We'll place some constants here

// Define needed styles
const styles = StyleSheet.create({ });

class Placeholder extends Component {
    // Initial state
    state = { };

    // Step #4 (Self-bound function)
    // We will update the state of the application so that images can render
    onAfterLoad = (data) => { };
    
    // Step #1 & #2
    // Here we’ll fetch JSON for images to be displayed
    componentWillMount() { }

    // This will be responsible for rendering the image components
    buildImages(data) { }

    // Step #3 and #5
    // Render the main view and child images
    render() { }
}

How to setup image placeholders

One of the best ways to envision how this will work is thinking about a timeline.

How to Use Image Placeholders in React Native 3

The steps break down like this:

  1. componentWillMount() executes. This gives us some intelligence that our component will come to life, so we kick off the load, which is the next step.
  2. Inside the body of componentWillMount(), we’ll initiate a fetch() for the JSON data. Keep in mind that these requests are asynchronous, making the next step rather crucial for two reasons. First, we have no idea how long it’s going to take for the fetch() to finish. Second, if we don’t show something meaningful, the user won’t get the rapid visual feedback that they have become accustomed to with high-performing mobile applications. Without that feedback, they will get the wrong impression of the application’s ability to function and may elect to close it.
  3. In this step, the render() phase of the component lifecycle kicks in. What you must keep in mind is that we don’t have data to display just yet, so we’ll have to show the user something. We could display a loading indicator, but for some application designs, that pattern won’t work. So we’re going to render enough placeholders to fill the screen.
  4. Assuming step #2 completes successfully, the fetch() success callback handler will trigger a new state, passing in the newly fetched data. For simplicity’s sake, I did not put much error handling here.
  5. This step calls this.setState() with an updated state object containing the image objects as a member labeled data, forcing a refresh of the render cycle.
  6. Now that we have data in our state object, we’ll render <Image/> components with both the placeholder image and the actual source URI will point to the images the user will want to see.

Here is a video that demonstrates what rendering of the view looks like with and without the placeholder images. On the left, you’ll find a version of the application that only renders the <Image/> components after the fetch has successfully completed, versus the two-pass approach described above.

Here are some things to think about as you review the code below.

  • You’ll find that the buildImages() method, which is called by render(), is responsible for iterating over the data and producing instances of <Image/>. It determines if we have data in the local state object (line #92) and will pad the array with ten undefines.
  • When the fetch() completes, we’ll enter the second phase of the render cycle, where we expect to have data with URIs for the images we expect to render. buildImages() will at that point use the data to configure the images to the proper URLs.
  • The last thing to think about is that we must set the defaultSource prop for the <Image/> component to the placeholder. This allows the <Image/> components to fetch the image assets, while allowing to display something for the user.
  • To exacerbate the situation, I’m using Math.random() as a cache buster and am simulating 3G network conditions with information from this blog post. Afterall, slow network is exactly why we’re doing all of this work.

There you have it. The multi-phase approach to rendering images with placeholders while we wait for data to come in is not a difficult approach to configure in our applications.

Conclusion

In this article, we explored using a multi-phase render approach to a view that renders two columns of images using placeholders to assist with the user experience. In the next post, I’ll work to tackle this approach coupled with animating the fade-in of the images.

Thank you for reading! Has this article helped you? Please share it via social media channels.

Have questions or any suggestions for improvement? Post them below.

  • lar

    Thanks for the tutorial. Would you know where to look to do the same but for a component? Thanks

  • lar

    Thanks for the tutorial. Would you know where to look to do the same but for a component? Or maybe describe how I would go about doing it? Thanks

    • DanComan

      renderData = (data) => {
      return (

      {data.name}

      // This is what I add to display the image

      );
      }

      renderPage() {
      return (

      Occasions

      );
      }

      var styles = StyleSheet.create({
      container: {
      flex: 1,
      },
      list: {
      flex: 1,
      flexDirection: ‘row’,
      flexWrap: ‘wrap’,
      justifyContent: ‘center’
      },
      box: {
      width: 150,
      backgroundColor: ‘red’,
      height: 150,
      alignItems: ‘stretch’,
      margin: 3
      },
      boxImage: {
      flex: 1,
      width: 150,
      height: 150,
      },
      boxText: {
      flex: 1,
      fontWeight: ‘900’,
      fontSize: 15,
      color: ‘white’,
      position: ‘absolute’,
      bottom: 5,
      right: 5,
      backgroundColor: ‘rgba(0,0,0,0)’
      }
      });

  • DanComan
  • dongron

    defaultSource works only on iOS. How to reproduce it on Android?


Like What You See?

Got any questions?