TL;DR: While Partytown significantly boosts PageSpeed by offloading scripts to Web Workers, it effectively cuts off direct access to GTM from the main thread. To fix broken gtag() and dataLayer.push() calls, you must explicitly enable event forwarding in your Partytown configuration.
When using Partytown to migrate third-party scripts like Google Tag Manager (GTM) to a Web Worker, the main thread loses the ability to directly call gtag or dataLayer.push by default. To ensure custom events are reported correctly, you must explicitly forward these functions in astro.config.mjs, allowing them to execute within the Worker without blocking the main thread.
The Problem
To optimize Core Web Vitals, many developers use Partytown to offload resource-intensive scripts (like GA4, Clarity, or HubSpot) to a Web Worker. This strategy drastically reduces main thread blocking time, often resulting in a significant boost to mobile PageSpeed scores.
However, this isolation mechanism introduces a side effect: the main thread loses access to global variables defined by those scripts.
When you attempt to manually trigger events in your business logic:
// These calls execute on the main thread,
// but the GTM instance lives in the Web Worker.
gtag('event', 'login', { id: 'abc' });
window.dataLayer.push({ event: 'login', id: 'abc' });
Since the gtag or dataLayer instances managed by Partytown do not exist on the main thread’s window object, these calls will either throw a ReferenceError or fail silently, leading to lost analytics data.
Partytown Integration Basics
Partytown is a lightweight library designed to relocate third-party scripts to a Web Worker. Integrating it into an Astro project is straightforward:
npm install @astrojs/partytown
Once installed, you simply mark your scripts with type="text/partytown", and Astro handles the rest.
<script
type="text/partytown"
src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXX"
></script>
If you only rely on automatic events like “Page Views,” the configuration above is usually sufficient. However, if your application relies on custom user interaction events, you need to configure Event Forwarding.
The Solution: Configuring forward
To bridge the communication gap between the main thread and the Worker, we need to instruct Partytown: “If you see a dataLayer.push or gtag call on the main thread, capture the parameters and forward them to the Worker.”
Modifying astro.config.mjs
In your Astro configuration file, locate the partytown integration settings and add the forward array:
import { defineConfig } from 'astro/config';
import partytown from '@astrojs/partytown';
export default defineConfig({
integrations: [
partytown({
config: {
// Key Config: Forward calls from main thread to the Worker
forward: ['dataLayer.push', 'gtag'],
},
}),
],
});
How It Works
Once configured, Partytown creates proxy objects for dataLayer and gtag on the main thread’s window. When your front-end code calls gtag(...), Partytown intercepts the request, serializes the data, and transmits it to the Web Worker where the actual GTM instance resides.
This approach allows you to enjoy the performance benefits of Web Workers while maintaining full data tracking capabilities.