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.
Would you be interested in joining LogRocket's developer community?
Join LogRocketโs Content Advisory Board. Youโll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.
3 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
Have you successfully used partytown with LockRocket itself? I’m running into issues right out of the gate.