A Short Intro to Partytown

It happens all the time. We try to optimize our code, optimize how images and CSS are loaded, do code splits, and all kinds of smart techniques. And in most cases, all this effort is meaningless because we need to load a ton of 3rd party libraries.

We have to do that not because we want it but because the business requires it. They need tools to count users, determine what users like and where are they coming from, track them, increase engagement and so. These tools mean 3rd party JavaScript libraries that must be loaded on the website. Typically, the size of 3rd party JavaScript loaded on a company website is much bigger than all the JavaScript used for actual website functionality.

It’s not uncommon to have several Megabytes of 3rd party JavaScript!

I dread those 3rd party scripts. They are mocking me and they ridicule my work: the websites are crazy slow especially on mobile. The most ironic part is when some of these tools present graphs with how much money you lose because your website is not fast enough! No shit.

Until now. Because now I think I’ve found a solution to this madness. And it’s called Partytown.

Partytown’s philosophy is that the main thread should be dedicated to your code, and any scripts that are not required to be in the critical path should be relocated to a web worker. Into a sandboxed location, kinda like…a little town for third-party scripts. Some sort of a…Partytown, if you will… - Adam Bradley

Here is a visual explanation of how Partytown works, it loads 3rd party scripts in a separate thread, a web worker (source https://github.com/BuilderIO/partytown): How Partytown works

How to Add Partytown to a Project

Partytown uses a web worker and the web worker must run on your website, meaning there is no way to use it from a CDN or another server. I could not find a downloadable archive with all the stuff ready to use, the only way (at least for now) to use Partytown is to create an npm project and install the Partytown npm package. I’ll use partytown-demo folder.

Create a new folder for the project and then initialize it:

npm init

Inside the project folder install Partytown:

npm install @builder.io/partytown

Let’s also create an index.html file inside partytown-demo that will use the scripts:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Partytown demo</title>
</head>
<body>
  <h1>Partytown Demo</h1>
</body>
</html>

Now we need to modify the package.json to include the command that will copy Partytown files and the index.html in the release folder:

"scripts": {
  "start": "npm run partytown && cp index.html ./public",
  "partytown": "partytown copylib public/~partytown"
},

To test if all works, run the following command:

npm start

There will be a public folder inside partytown-demo with the following content:

/public
  /~partytown
    partytown-atomics.js
    partytown-media.js
    partytown-sw.js
    partytown.js
  index.html 

How to Use Partytown

Normally 3rd party scripts are loaded on the main thread:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Partytown demo</title>
</head>
<body>
  <h1>Partytown Demo</h1>
  <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
  new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
  j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
  'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
  })(window,document,'script','dataLayer','GTM-XXXXXX');</script>
  </script>
</body>
</html>

In order to use Partytown you need to do two changes:

  1. Import the Partytown script
  2. Set type=“text/partytown” for the 3rd party scripts
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Partytown demo</title>
</head>
<body>
  <h1>Partytown Demo</h1>
  <script src="~/partytown/partytown.js"></script>
  <script type="text/partytown">(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
  new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
  j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
  'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
  })(window,document,'script','dataLayer','GTM-XXXXXX');</script>
  </script>
</body>
</html>

And you’re done.

If you’re using a framework check Partytown docs because Partytown has several integrations for some frameworks like Gatsby or NextJS, I’m sure more will be added in the future.

Trade-Offs

For this to work we need to add a new 6kb JavaScript library, the Partytown lib. So there are cases when using Partytown will actually degrade performance. Just to make sure we’re not shooting ourselves in the foot we need to do a performance test before and after.

When I’m writing this Partytown is still in Beta, so some things might change.

Resources