Editor’s note: This article was updated on 12 May 2022 to include more up-to-date information about the Partytown library.
Web workers were met with high expectations after their introduction in 2010. However, as time went on, even though developers used web workers for advanced libraries and use cases (like ML training in TensorFlow or language support in Monaco Editor), they became a last-resort optimization technique for everyday projects. This status quo became only more apparent as Web Assembly (Wasm), and its later support for multi-threading, was introduced.
With that said, there’s a new library called Partytown that aims to use web workers to solve a common problem: moving intensive third-party scripts execution off the main thread. Let’s take a closer look.
Before we talk about Partytown, we should understand its goal first.
Third-party scripts are a common sight across the vast majority of websites. They’re responsible for injecting analytics (Google Analytics), ads (Google Adsense), processing payments (Stripe), displaying embeds (Disqus), and more. In general, creating a modern web app is almost impossible without including at least a single third-party script in your code.
But while third-party scripts are extremely helpful, they have one enormous disadvantage: they hurt your website’s performance. With each script often being a multi-kB or even MB-sized “black box” of code, it’s no wonder that with just a few of them, your website will scramble.
This is where Partytown comes into play.
Partytown is a new library with the goal of speeding up your website by decluttering the main thread from the burden of third-party scripts. It aims to achieve this with the use of web workers.
On the surface, it seems like a simple idea. Just take a script and put it inside a web worker, right? Not really — issues start to arise when we take into account the limitations of web workers.
Web workers can’t:
window
object’s methods and propertiesAs you can imagine, these limitations make most third-party scripts impossible to run from a web worker on its own — that is, without making additional adjustments.
To allow your third-party script to run inside a Web Worker, Partytown uses a few tricks:
type="text/partytown"
attribute on the main thread, fetches their content, and runs them inside a web worker through Blob
sProxy
objects for accessing the DOM and other globals available in the main thread. These objects handle communication with the main thread and execute actions that can’t be done from within a web workerAs you can see, there are many workarounds involved to make Partytown possible. Add to that the sandboxing iframe
that wraps the web worker, and you can see how complex of a project Partytown really is. That’s partially why the library is still in beta and might not yet work “out-of-the-box” in some cases.
With that said, there are a couple of integrations already tested by the Partytown development team, and we’ll be taking a look at one of them to see how the library works.
Partytown provides configurations for tested services, like HubSpot Forms, Intercom, and Google Tag Manager (GTM).
Before you get to the code, you first need to set up your environment. Create a new npm project and install the Partytown module:
npm init -y npm install @builder.io/partytown
Now, that was pretty normal for any modern JavaScript library. However, the next, more unconventional step is to get the lib
directory’s content from the @builder.io/partytown
npm module available through the /~partytown/
route of your website.
If your setup uses, for example, a public
directory for that, you can do it with a simple command provided by Partytown CLI:
partytown copylib ./public/~partytown/
For popular module bundlers like Vite, Rollup, or webpack, Partytown comes with dedicated plugins.
With the module and lib
’s content in place, you can now initialize Partytown. To do so, you have to inline the Partytown snippet into your page:
import { partytownSnippet } from '@builder.io/partytown/integration'; const snippetContent = partytownSnippet(); const script = document.createElement("script"); script.textContent=snippetContent; document.body.append(script)
You can use the above code to initialize Partytown in any JavaScript app. Partytown also provides dedicated integrations for popular UI frameworks.
If you want to configure Partytown to turn on the debugging mode or customize lib
’s content location, you should declare a global partytown
object with the necessary options:
window.partytown = { debug: true, };
With Partytown correctly set up, we’ll now integrate HubSpot Forms.
First, prepare your HubSpot account, an example form, and its embed code. Now, just drop the HubSpot snippet into your HTML file and add the type="text/partytown"
attribute to all the <script>
tags
<!-- ... --> <script charset="utf-8" type="text/partytown" src="//js-eu1.hsforms.net/forms/shell.js" ></script> <script type="text/partytown"> hbspt.forms.create({ region: "eu1", portalId: "25225394", formId: "cd2ac1fe-aad5-4959-a6ad-5409ba10bc44", }); </script> <!-- ... -->
In the ideal scenario, you’d expect the integration to be already done and everything to work well. Sadly, due to the very experimental, “bleeding edge” nature of Partytown, that’s not the case.
Even if the form appears just fine, open the dev console, and you’ll be greeted with a red wall of errors. Some could be CORS, others rendering-related.
CORS errors are thrown when Partytown attempts to fetch a cross-origin resource. As the request is now made through a web worker that on its own comes from an iframe
, it’s no wonder CORS issues can arise. That’s why Partytown utilizes a proxy service to fall back to after the request fails. But still, the error message remains.
To make the CORS issues disappear, you can pre-download the file and serve it from your origin. You should do the same for all sub-requests of the pre-downloaded scripts as well. However, keep in mind that not all resources can be pre-downloaded, and sometimes such workarounds can break the third-party scripts because of Content Security Policy (CSP) restrictions.
Other, less notable errors don’t affect the functioning of the embed. With CORS fixes implemented, the embed should render and send submissions just fine.
With this integration, we can now test if Partytown works.
To the naked eye, it might actually feel slower. That’s because Partytown’s DOM operations are purposefully throttled. This might not always be desired, but it helps with limiting third-party scripts from continuously blocking the main thread.
When we take a look at the Performance tab in Chrome DevTools, we’ll see Partytown shine.
While differences were negligible at first, with six times the CPU slowdown, Partytown’s advantage becomes truly visible. With over four times less time spent on scripting and 0ms vs. 297ms blocking time over the standard method, Partytown has successfully moved the impact of the third-party script away from the main thread.
So, as you can see from our walkthrough, even though it’s working, Partytown isn’t production-ready right now. While HubSpot Forms might work with only some issues, there are many other untested third-party scripts that won’t work at all.
Other notable drawbacks include:
event.preventDefault()
not workingPartytown works. The idea behind it is excellent, and it achieves its goal. However, given that it’s a very complex library that uses many workarounds to get third-party scripts working inside web workers as intended, its beta status really means beta. Partytown, while on the right track, isn’t production-ready right now.
Overall, it’s a bit concerning that such a library is necessary. As we increasingly depend on third-party scripts, and their performance impact only grows, the speed and efficiency of many modern websites starts to be concerning. Naturally, the best — but also most demanding — solution would be for all script vendors to better optimize their code.
Without that, Partytown and others like it seem like the only option to vastly improve the performance of many websites. And it would do so without requiring each third-party script to be optimized individually. Fingers crossed 🤞
There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.
LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.
LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.
Build confidently — start monitoring for free.
We show how to use Goxgen to scaffold a full-stack React app. See how to integrate React with Go and modify Goxygen to suit your project requirements.
Express.js is a Node.js framework for creating maintainable and fast backend web applications in JavaScript. In the fast-paced world of […]
Web components are underrated for the performance and ergonomic benefits they provide in vanilla JS. Learn how to nest them in this post.
Angular’s new defer
feature, introduced in Angular 17, can help us optimize the delivery of our apps to end users.
2 Replies to "Using web workers to boost third-party script performance"
‘GoogeTagManager’ isn’t provided with partytown/react
When is this going to be ready? If i give you money will it be ready sooner? Or my first born child?
I have a kidney and a spleen you can take if it will speed things up please make this third party script nightmare go away