paint-brush
Improve Your UX by Dynamically Rendering Images via Reactby@andrewwong
26,675 reads
26,675 reads

Improve Your UX by Dynamically Rendering Images via React

by Andrew WongMarch 14th, 2017
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

It’s a competitive world out there right now. As we all know, just having a good idea isn’t enough to make a company the next billion dollar IPO. Execution is just as important, and when it comes to product, that boils down to one factor — <em>User Experience</em>.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Improve Your UX by Dynamically Rendering Images via React
Andrew Wong HackerNoon profile picture

It’s a competitive world out there right now. As we all know, just having a good idea isn’t enough to make a company the next billion dollar IPO. Execution is just as important, and when it comes to product, that boils down to one factor — User Experience.

User Experience is a lot more than how our product looks aesthetically. It’s also how performant it is, how intuitive it is — how much it delights our users.

We’ve all been there, discovering a new app or web page for the first time and seeing something like this:

With high resolution photos and retina screens, it’s all too often we have to sit and watch images painstakingly render. It’s a common sight to see an image slowly rendering from top to bottom.

This problem can be solved.

The first 2 obvious optimizations are to use a CDN and utilize caching (your browser automatically does this) to make images load faster. However, we can also trick our user’s perception of load time which results in an overall positive increase in user experience.

Here are 2 tricks we can use to improve our UX on media-rich apps:

1. Use a placeholder:

use a placeholder to indicate media is loading

A placeholder is modern twist on the classic loading spinner. Rather than a generic spinner to indicate the app is loading, we use a placeholder that communicates to the user what type of content is loading — images.

Facebook and LinkedIn are both good examples that use this technique to improve their UX.

2. Dynamically adding images to the DOM:

The second optimization is to fully download our images before showing them on the screen. This will avoid the classic top-to-bottom rendering we’re used to seeing for images as they’re being downloaded. We accomplish this by using React’s [onLoad](https://facebook.github.io/react/docs/events.html#image-events) event; we can make the request to the server for the image files, but not render the image in the DOM until the entire file has been downloaded.

the end result: smooth as butter

The end result is an app that loads high resolution images and never keeps the user waiting. The placeholder teases the user and lets them know images are being loaded. Furthermore, we hold off on rendering the images until they have been fully downloaded from the server so our user never has to see images painting from top to bottom in the browser.

view live demo

Show Me the Code!

Rendering the Placeholder

For our placeholder component (LoadingItem in this example), we simply render the image and apply any animation effects we want:












export default function () {return (<ReactCSSTransitionGrouptransitionName="loadingItem"transitionAppear={true}transitionAppearTimeout={500}transitionEnterTimeout={500}transitionLeaveTimeout={300}><img className="feed__loading-item" src={img} /></ReactCSSTransitionGroup>)}

In the render of our Feed component, we simply render LoadingItem as long as we still have FeedItems being loaded:

export default class Feed extends Component {

...



render() {return (<div className="feed">

    ...

    {this.props.items.length > this.state.loadedItems.length &&  
      <LoadingItem />  
    }

    ...

  </div>  
)  


}}

Dynamically Rendering Images via `onLoad`

Our Feed component works as follows:





export default class Feed extends Component {constructor(props) {super(props)this.state = { loadedItems: [] }}





onLoad(feedItem) {this.setState(({ loadedItems }) => {return { loadedItems: loadedItems.concat(feedItem) }})}


























render() {return (<div className="feed"><h1 className="feed__h1">{this.props.name}</h1>{this.state.loadedItems.map((item, i) =><FeedItemimgPath={item.imgPath}name={item.name}renderModal={this.props.renderModal}key={i} />)}{this.props.items.length > this.state.loadedItems.length &&<LoadingItem />}<div className="hidden">{this.props.items.map((item, i) =><imgsrc={item.imgPath}onLoad={this.onLoad.bind(this, item)}key={i} />)}</div></div>)}}

So what’s happening here? We have a hidden <div> at the bottom that is responsible for downloading the image files. When the file is finished downloading, the onLoad event will trigger, which updates the newly loaded item in the state. When the state updates, the newly loaded item is rendered into the DOM with the image already fully downloaded.

That’s it!