Loading Third-Party Scripts In React
Learn how to properly load 3rd party scripts in React, improve performance, and avoid nasty bugs. (4 minutes)
A few weeks ago, I had to deal with a nasty bug in a React SPA application.
After thorough debugging and investigation, I found that it is related to how we load and use third-party scripts.
In today’s article, I’ll share my findings and different ways of loading third-party scripts based on the desired outcome.
As you can imagine, there’s no single best approach.
It really depends on what we’re trying to achieve and what trade-offs we’re ready to make.
Let’s first see the bug I was facing.
Uncaught Reference Error: XXX is not defined
This was the error I was getting from time to time. It was a sporadic failure.
For context, Chargebee is a 3rd party tool for dealing with payment methods, cards, subscriptions, etc.
Regarding the application, there’s a billing details page where customers are prompt to enter their billing card.
We were loading a chargebee.js script so we can embed their iframes.
After the script is loaded, a Chargebe object is attached to the window object and can be used for displaying the iframes or triggering other functionality.
As reading the error, it appeared that sometimes the Chargebee object is not added to the window object.
This made me think that the way we load the chargebee.js script is not optimal and there’re unhandled corner cases.
Loading 3rd Party Scripts Through useLayoutEffect and Delay
After some debugging, I’ve found the piece of code which was responsible for loading all 3rd party scripts, including chargebee.js.
This code was being executed when the whole React application is bootstrapped.
useLayoutEffect(() => {
const timer = setTimeout(() => {
INITIAL_SCRIPTS_TO_PREFETCH.forEach(({ url, fileUrl }) => {
prefetch(url, fileUrl, 'script');
});
}, 1200);
return () => {
clearTimeout(timer);
};
}, []);
The code inside useLayoutEffect runs synchronously after React updates the DOM.
It sets a timer to delay the third-party scripts loading.
This improves the overall application performance because the application is not blocked to wait for these scripts to load and can continue rendering the entire tree.
That’s a smart way to load third-party scripts and will work 99.9% of the cases, especially if the script is not critical.
However, there’s one caveat. What will happen if the first page we want to open is using one of these scripts?
Well, we’ll probably get an error because the delay of 1.2 seconds might be too big.
So this was causing the Uncaught Reference Error: XXX is not defined error from above.
If we want to ensure that the script is loaded, we must look for another way to achieve that.
Loading 3rd Party Scripts Through Conditional Rendering and Loading
To mitigate the error from above, we must ensure that the script is loaded before referring the window.Chargebee object.
Otherwise, we will get the same error: Uncaught Reference Error: Chargebee is not defined.
Also, we don’t want to downgrade the current performance.
Loading the script before the app is bootstrapped is not an option since this script is necessary for 1% of the pages.
It is the easiest option but not the best one.
For example, it’s not okay to add the script loading inside the index.html file because we can’t delay its loading and will hurt the performance.
Since the script is used only on a small number of pages, we can create a custom hook responsible for its loading.
We can expose a loading state which can be used by the clients of the hook.
This is another great way to load a 3rd party script and provide a feedback of whether the script is loaded or not.
This way, we provide the flexibility to the clients of our custom hook to do whatever they want - render a spinner, return a fallback message, etc.
We are using the <script>’s onLoad functionality to know that the script is loaded successfully.
The approach is suitable for corner cases where the loading script is critical and might break the UI.
In our case, if the user is prompted directly to the BillingDetails page, they will get an error because the script is not loaded.
For other scenarios, this overhead and complexity is not needed and recommended.
📋 Recap
Loading third-party scripts the right way is essential to ensure good UX and application performance.
Use useLayoutEffect + Delay for loading 3rd party scripts when they’re not critical to the application business logic.
Use conditional rendering + Loading inside custom hook for loading 3rd party scripts when you want to make sure the script is loaded and provide a feedback.
That's all for today. I hope this was helpful. ✌️
👋 Let’s connect
You can find me on LinkedIn, Twitter(X), Bluesky, or Threads.
I share daily practical tips to level up your skills and become a better engineer.
Thank you for being a great supporter, reader, and for your help in growing to 19.3K+ subscribers this week 🙏
You can also hit the like ❤️ button at the bottom to help support me or share this with a friend. It helps me a lot! 🙏
Excellent performance improvements Petar friend!