Can we use Google Tag Manager from a web worker? Is <script type="text/partytown">
a new standard? Let's see what this new script
type is and how we can easily move workloads from the main thread to a worker with an example using GTM and Next.js. After that you can implement this with vanilla JS and other third party scripts like Facebook Pixel, Mixpanel, Amplitude, etc.
What is Partytown ๐?
Partytown is a library that helps us to relocate scripts into a web worker creating communication channels, like dataLayer.push()
which is used in GTM. If GTM was loaded into a worker, every call to dataLayer.push()
should be forwarded from the main thread to the worker thread, and Partytown solves that automatically! Also, it allows code executed from the web worker to access DOM synchronously, so third-party scripts can run exactly how they were coded and without any alterations.
Creating script tags for workers
To run code in the worker just use type="text/partytown"
. The browser wonโt recognize that type ignoring the script
tag and then Partytown will load that script
inside a web worker. This works for inline code or files.
<script type="text/partytown">
/* GTM Script Here */
</script>
<script type="text/partytown" src="https://example.com/analytics.js"></script>
Where is the forward
?
To forward
a function call to the worker we just need to add the configuration to the window
object before the inlined Partytown script
.
<script>
window.partytown = { forward: ['dataLayer.push'] };
</script>
<script type="text/partytown">
/* GTM script here */
</script>
Now, you can use window.dataLayer.push()
from the main thread. ๐
Using workers in Next.js
Setting up Partytown in a Next.js website is very easy! We just need Next.js v12.1.1 or later.
๐ง Remember that this is an experimental feature in Next.js and itโs not covered by semver. ๐
First, we should enable the experimental flag in the next.config.js
file.
module.exports = {
experimental: { nextScriptWorkers: true }
};
And install the Partytown package.
npm install --save-dev @builder.io/partytown
The Next.js workers don't require extra configurations, but we should forward
the dataLayer.push
for this example. We need to include this configuration in a <Head>
component inside a custom _document.js
.
import { Html, Head, Main, NextScript } from 'next/document'
const Document = () => (
<Html>
<Head>
<script
data-partytown-config
dangerouslySetInnerHTML={{
__html: `
partytown = {
lib: '/_next/static/~partytown/',
debug: true,
forward: ['dataLayer.push']
};
`
}}
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
export default Document;
๐ก The debug: true
setting gives us nice info in the DevTools' console.
Finally, we can add the GTM script in our _app.js
.
import Script from 'next/script';
const MyApp = ({ Component, pageProps }) => (
<>
<Script
id="gtm-script"
strategy="worker"
dangerouslySetInnerHTML={{
__html: `/* GTM script here */`
}}
/>
<Component {...pageProps} />
</>
);
export default MyApp;
The id
property is required for inline scripts in order to let Next.js track and optimize the script.
And it's done! Your GTM runs in a worker that you can communicate with using the dataLayer.push
- as always.
You can find installation instructions for React, Angular, Nuxt and more.
Conclusion
Too much JavaScript can block DOM construction, delaying how quickly pages can render. Reducing JavaScript in our main thread is a good strategy to reduce the time until interaction. This will free up main thread resources so they can be used for the primary web app execution, e.g. network requests.
With Partytown we can use read and write main thread DOM operations synchronously from within a web worker; I wonder if this can be used in a microfrontend application to increase the responsiveness of our website.
I hope you find this helpful If you came here looking for performance improvements for your website. Do you have production experience with workers? Have you used Partytown before? Tell me in the comments.