How to Preload Images into Cache in React JS

React Logo
React Logo
https://cdn4.iconfinder.com/data/icons/logos-3/600/React.js_logo-512.png

I ran into a problem recently, where the large, high-resolution background images in my web app were loading slowly and, as a result, the webpage would look glitchy. Here is an example of what it would look like:

Image for post
Image for post

After researching this, I discovered a few possible solutions.

One of the solutions is that I could implement a Lazy Loading which would load the image as a blur and it would slowly focus in on the image and eventually remove the blur altogether. I would either need to code my own Lazy Loading component or I would need to use a Lazy Loading NPM package, such as react-lazyload.

The other solution that I discovered, which is more traditional, is that I could preload the large, high-resolution images into the browser cache prior to rendering the web app.

I ended up deciding to go with the more traditional approach, preloading the images into the browser cache.

In order to do this, I need to adjust my App.js file. To start, I need to import in a Spinner, useEffect, and useState.

Next, I will need to declare one new variable in my state with the useState hook, the isLoading element.

useState declaring isLoading/setIsLoading
useState declaring isLoading/setIsLoading

From there, I need to create a call of useEffect and utilize an empty array in the dependency argument so that it acts as a componentDidMount and runs only once. In this call of useEffect, I want to have an array of all of the images that I want to preload, which in this case are all of the large, high-resolution images that my web app uses. I then pass this image array into a function, which we will create next, called cacheImages

array of image links that are then executed in cacheImages function, all in useEffect hook
array of image links that are then executed in cacheImages function, all in useEffect hook

Next, we are going to craft our cacheImages function. This function will take in an image array as an argument and will loop through the array with Map. In each iteration, the current image will be processed and loaded into a Promise. It is done in a Promise, so that the load fully completes before jumping to the next image (this will happen once we run Promise.all(promises) where all of the promises run). To load the image into the class, we will simply declare a new Image instance, and will attach the image url to that instance. Then we will set the Promise’s resolve attribute to the Image onload attribute and we will set the Promise’s reject attribute to the Image onerror attribute. Lastly, I set the isLoading state element to false.

Image for post
Image for post

Lastly, you return all of the promises to a variable and then run Promise.all on the promises variable. Promise.all will need to be awaited, and there you have it, your images are now loaded into the cache. Lastly, we have to make sure we do not show the web app until the images have loaded/till the isLoading is false. To do this, we will simply use a conditional in our return JSX like this to display a Spinner while the images are loading:

Conditional for isLoading where Spinner is shown when true, otherwise routes component is shown
Conditional for isLoading where Spinner is shown when true, otherwise routes component is shown

So while isLoading is true, a Spinner is showing, and when it changes to false, the page from the Routes component is showing.

I hope this explanation is helpful. I am not sure that this is the best practice for preloading images, but it is what ended up working for me in this situation, as I struggled immensely with getting Lazy Loading working. Thanks for reading, happy coding!

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store