React + Webpack: ChunkLoadError: Loading chunk X failed.

Raphaël Léger
8 min readMar 8, 2021

--

Loading chunk 6 failed.
(missing: https://WEB_SERVER.com/82fbafaa3a.CHUNK_NAME.js)

Getting this error message?
Follow this detailed guide to fully understand the problem and fix it.

It is not happening randomly

Sometimes none of your users encounter the error for a few weeks.
Sometimes your logs report that several users faced it within a few hours.

This looks random, right? It is not.

You get this error when React attempts to lazy load a file — eg. a component — but the file does not exist. As of the time of writing this article, most of the solutions found on the internet suggest to try/catch the error, retry a few times and then crash silently. This does not address the underlying issue at all. If you opt for this kind of “solution”, the error keeps happening, it gets hidden from your logs and your users still see your app crash.

The ideal scenario you thought about

Let’s assume you have a webpage with a component that is not displayed by default but gets lazy loaded whenever the user needs it — for instance when pressing a button, or when reaching a specific route.

Let’s also assume you have the following Webpack configuration:

a prevalent way of configuring the lazy load in webpack.config.js for a React app

An important thing to do is to focus on what happens when you build your app. Webpack will choose a random unique hash, for instance aaaaa and it will create the following three files:

1)aaaaa.component.js: a script containing only the code of the component

2) aaaaa.main.js: a script containing the main code of the webpage. In this file, the name of the file aaaaa.component.js is written — in a quite obfuscated way— and React will know it has to use this filename whenever it needs to lazy load the component.

somewhere in the middle of the file aaaaa.main.js

3) index.html : a static page that loads the script aaaaa.main.js

index.html after performing a build

So when you open up the index.html in a browser, the aaaaa.main.js script gets loaded and you see your app.

Later on, whenever a user needs your component, the main script aaaaa.main.js lazy loads the component script aaaaa.component.js and the component is properly displayed.

Everything works as expected. This is the ideal scenario.
Awesome, both the user and your page can live happily ever after.

Moving on, let’s get to the cases where this does not work.

The two edges cases you may have forgotten

Case #1: A user is using your app while you deploy a new version

Let’s say you deployed a first version of your app. On your server there are those three files that are working together:

  • index.html
  • aaaaa.main.js
  • aaaaa.component.js

Let me introduce you to John, one of you users. He opens up your app in a browser and so he gets served the static page index.html that loads the script aaaaa.main.js

John has not yet triggered the action to display the component, so the lazy load of the component has not happened yet. In other words, the file aaaaa.component.js referenced by aaaaa.main.js has not been loaded yet.

Now, for some reason, you need to build and deploy a new version of your app. Webpack will create three files the same way it did before, only this time it will use a new hash, for instance bbbbb:

  • bbbbb.component.js: a script containing the new code of the component
  • bbbbb.main.js: a script containing the new main code of the webpage. In this file, the name of the file bbbbb.component.js is written
  • index.html: a static page that loads the script bbbbb.main.js

When deploying this new version, you usually remove the old index.html, the old aaaaa.main.js and the old aaaaa.component.js. Then you put the three new files online:

  • index.html
  • bbbbb.main.js
  • bbbbb.component.js

Any new user reaching your app should be served the latest index.html file, that will load the new bbbbb.main.js file that will later on lazy load the new bbbbb.component.js and your app will work as expected.

But do not forget John! John still has its browser opened up with the old index.html file and most importantly its browser had loaded the old script aaaaa.main.js that thinks it has to load aaaaa.component.js whenever it needs to lazy load the component.

A few moments later, John finally needs to display the component.
The file aaaaa.main.js attempts to lazy load aaaaa.component.js — that does not exist anymore since you removed it from your server to replace it by bbbbb.component.js and… guess what John sees… ?

Loading chunk 0 failed.
(missing: https://WEB_SERVER.com/aaaaa.component.js)

Case #2: A user is on a browser that decided to cache index.html

Remember when I said that any new user reaching your app should be served the latest index.html file you deploy?

While it is true for the very large majority of users, some specific users are actually not served the latest index.html file.

The poor John for instance, he decided to leave and close your app after seeing it crash. But, as the reckless person he is, he decided to come back a few days later to see whether the lazy load would work this time.

Even though you deployed your new index.html along with bbbbb.main.js and bbbbb.component.js, John’s browser remembers that he visited your app a few days before.

Knowing this, John’s browser decides that it will load the index.html file from the cache and show it up to John, hence not getting the latest version available. John’s index.html old version still loads aaaaa.main.js — also cached!— and this aaaaa.main.js will still try to load aaaaa.component.js and… guess what John sees… ?

Loading chunk 0 failed.
(missing: https://WEB_SERVER.com/aaaaa.component.js)

Bye John. He was a nice guy, but not being able to use your app for a whole week without crashes…? Made him leave. Hope he’s doing okay on his other projects, using other apps.

A note on why some browsers cache static pages by default and some don’t:

Usually, nowadays browsers don’t cache index.html and if you refresh the page, they will ask the server for the latest version almost every single time.

By default, it is every browser’s responsibility to define when they use the cache and when they ask the server for the latest version.

Big companies love to bring their own customized version of browsers to their employees and sometimes it implies messing up with the cache; there may be a higher chance for users on those browsers to encounter this kind of errors due to a hardcoded default caching duration that is way too long.

An easy light fix

Handling both case #1 and case 2

When deploying a new version of you app, just keep the old JavaScript files — at least the ones that need to be lazy loaded — instead of removing them. In our example using version aaaaa and bbbbb, after deploying the second version there would be the following files on your server:

  • index.html
  • aaaaa.main.js
  • aaaaa.component.js
  • bbbbb.main.js
  • bbbbb.component.js

=> Users getting served the latest version ofindex.html will correctly see the latest version of your app, without any error.
=> Users whose browser is still serving a previous version of index.html will see the old version of your app, without any error.

Though… there are a few things to consider before performing this fix.

Do you really want some of your users to still access the old versions of your app? Are you sure that the previous versions of your frontend are compatible with the latest backend API endpoints? Do you really want to receive logs and potentially get feedbacks from users that are not using the latest version?

If the answer is no — it probably is, check out the following section regarding a better fix.

An easy and better fix

Handling case #2: Prevent the index.html file to be cached at all

In a React application, the index.html file is very small as almost all the logic resides in the JavaScript files. In this file, add the following meta tags:

the meta tags to add in your index.html to prevent the page from being cached

Those two tags make sure that any browser visiting your app always gets the latest version of the index.html file, may it be the user’s first visit or a returning user that already saw an old version of the app some time ago.

Handling case #1: Force the refresh of the page ONCE when you encounter this error

The key is not to retry over and over hoping for the missing script to magically exist. Though, now that you have made sure that the index.html shown to the user will always be the latest index.html deployed on the server, you can force a page reload once to make sure that the user is on the latest version of the app.

In order to make sure you don’t trap users in an infinite loop, you should use a system such as the local storage and set a flag saying whether the app has already been force-refreshed once or not yet.

Here is a code snippet of a function allowing the browser to refresh itself once that you can get inspiration upon:

In your React code, replace lazy by lazyWithRetry:

From now on, whenever a file fails to get lazy loaded, a refresh of the page will occur, making sure that users reach the latest version of the app. The latest version of index.html points to the latest JavaScript files, ensuring that the lazy loading works consistently.

If ever the lazy loading still raises an error even after a refresh, ie. for an other reason than the error fixed in this guide, you need to throw the error so that it shows up in your logs and does not crash silently.

Careful: when performing something such as a “force reload”, ensure that users do not lose their current progress in your app, for instance be sure to save the state of the app before forcing a reload and retrieve this state afterwards.

Conclusion

After reading this guide through, you should have a better understanding of why React and Webpack caused the error you stumbled upon. You should also have been able to implement one of the fixes proposed here, or even made your own specific fixes.

By the way, if this article helped you, don’t forget to clap & subscribe! 🙂
This helps me know that taking the time to write the article was worth it!

--

--