--- title: Partytown + Google Tag Manager in Hydrogen description: >- Add Partytown web worker integration for Google Tag Manager to improve performance source_url: html: 'https://shopify.dev/docs/storefronts/headless/hydrogen/cookbook/partytown' md: 'https://shopify.dev/docs/storefronts/headless/hydrogen/cookbook/partytown.md' --- ExpandOn this page * [Requirements](https://shopify.dev/docs/storefronts/headless/hydrogen/cookbook/partytown.md#requirements) * [Ingredients](https://shopify.dev/docs/storefronts/headless/hydrogen/cookbook/partytown.md#ingredients) * [Step 1: Ignore Partytown library files](https://shopify.dev/docs/storefronts/headless/hydrogen/cookbook/partytown.md#step-1-ignore-partytown-library-files) * [Step 2: Create GTM web worker component](https://shopify.dev/docs/storefronts/headless/hydrogen/cookbook/partytown.md#step-2-create-gtm-web-worker-component) * [Step 3: Document Partytown setup](https://shopify.dev/docs/storefronts/headless/hydrogen/cookbook/partytown.md#step-3-document-partytown-setup) * [Step 4: Add CORS reverse proxy](https://shopify.dev/docs/storefronts/headless/hydrogen/cookbook/partytown.md#step-4-add-cors-reverse-proxy) * [Step 5: Configure CSP headers](https://shopify.dev/docs/storefronts/headless/hydrogen/cookbook/partytown.md#step-5-configure-csp-headers) * [Step 6: Add URL resolver for proxying](https://shopify.dev/docs/storefronts/headless/hydrogen/cookbook/partytown.md#step-6-add-url-resolver-for-proxying) * [Step 7: Initialize Partytown and GTM](https://shopify.dev/docs/storefronts/headless/hydrogen/cookbook/partytown.md#step-7-initialize-partytown-and-gtm) * [Step 8: Enable atomic mode](https://shopify.dev/docs/storefronts/headless/hydrogen/cookbook/partytown.md#step-8-enable-atomic-mode) * [Step 9: Install Partytown](https://shopify.dev/docs/storefronts/headless/hydrogen/cookbook/partytown.md#step-9-install-partytown) * [Step 10: Configure Vite for Partytown](https://shopify.dev/docs/storefronts/headless/hydrogen/cookbook/partytown.md#step-10-configure-vite-for-partytown) * [Next steps](https://shopify.dev/docs/storefronts/headless/hydrogen/cookbook/partytown.md#next-steps) # Partytown + Google Tag Manager in Hydrogen This recipe integrates Partytown with your Hydrogen storefront to run Google Tag Manager and other third-party scripts in a web worker, keeping the main thread free for critical rendering tasks. Key features: * Moves GTM and analytics scripts off the main thread * Improves Core Web Vitals scores * Maintains full GTM functionality * Includes CORS reverse proxy for third-party scripts * CSP headers configured for GTM domains Note TypeScript users need to manually add GTM types to `env.d.ts`. *** ## Requirements * Google Tag Manager container ID (remember to set your `GTM_CONTAINER_ID` or `GTM_ID` environment variable) * Basic understanding of web workers and CSP * Node.js 18.0.0 or higher *** ## Ingredients *New files added to the template by this recipe.* | File | Description | | - | - | | [app/components/PartytownGoogleTagManager.tsx](https://github.com/Shopify/hydrogen/blob/12374c8f03f82c6800000cf08e327c4db4c287bb/cookbook/recipes/partytown/ingredients/templates/skeleton/app/components/PartytownGoogleTagManager.tsx) | Component that loads GTM scripts in a web worker via Partytown | | [app/routes/reverse-proxy.ts](https://github.com/Shopify/hydrogen/blob/12374c8f03f82c6800000cf08e327c4db4c287bb/cookbook/recipes/partytown/ingredients/templates/skeleton/app/routes/reverse-proxy.ts) | Reverse proxy route for third-party scripts requiring CORS headers | | [app/utils/partytown/maybeProxyRequest.ts](https://github.com/Shopify/hydrogen/blob/12374c8f03f82c6800000cf08e327c4db4c287bb/cookbook/recipes/partytown/ingredients/templates/skeleton/app/utils/partytown/maybeProxyRequest.ts) | URL resolver to control which scripts should be reverse-proxied | | [app/utils/partytown/partytownAtomicHeaders.ts](https://github.com/Shopify/hydrogen/blob/12374c8f03f82c6800000cf08e327c4db4c287bb/cookbook/recipes/partytown/ingredients/templates/skeleton/app/utils/partytown/partytownAtomicHeaders.ts) | Helper utility to enable Partytown atomic mode for better performance | *** ## Step 1: Ignore Partytown library files Add `public/~partytown` to ignore Partytown library files. #### File: [.gitignore](https://github.com/Shopify/hydrogen/blob/12374c8f03f82c6800000cf08e327c4db4c287bb/templates/skeleton/.gitignore) ([patch](https://github.com/Shopify/hydrogen/blob/12374c8f03f82c6800000cf08e327c4db4c287bb/cookbook/recipes/partytown/patches/.gitignore.0c7440.patch)) ```diff @@ -4,6 +4,7 @@ node_modules /build /dist /public/build +/public/~partytown /.mf .env .shopify ``` *** ## Step 2: Create GTM web worker component Add a GTM component that loads scripts in a web worker. #### File: [PartytownGoogleTagManager.tsx](https://github.com/Shopify/hydrogen/blob/12374c8f03f82c6800000cf08e327c4db4c287bb/cookbook/recipes/partytown/ingredients/templates/skeleton/app/components/PartytownGoogleTagManager.tsx) ## File ```tsx import {useEffect, useRef} from 'react'; /** * Component to add Google Tag Manager via Partytown * @see https://partytown.builder.io/google-tag-manager */ export function PartytownGoogleTagManager(props: { gtmContainerId: string | undefined; dataLayerKey?: string; }) { const init = useRef(false); const {gtmContainerId, dataLayerKey = 'dataLayer'} = props; useEffect(() => { if (init.current || !gtmContainerId) { return; } const gtmScript = document.createElement('script'); const nonceScript = document.querySelector('[nonce]') as | HTMLScriptElement | undefined; if (nonceScript?.nonce) { gtmScript.setAttribute('nonce', nonceScript.nonce); } gtmScript.innerHTML = ` (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.type = "text/partytown" j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl + '&version=' + Date.now(); f.parentNode.insertBefore(j, f); })(window, document, 'script', '${dataLayerKey}', '${gtmContainerId}'); `; // Add the partytown GTM script to the body document.body.appendChild(gtmScript); init.current = true; return () => { document.body.removeChild(gtmScript); }; }, [dataLayerKey, gtmContainerId]); if (!gtmContainerId) { return null; } return (