Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example: Partytown + Google Tag Manager + CSP #1548

Closed
wants to merge 12 commits into from

Conversation

juanpprieto
Copy link
Contributor

@juanpprieto juanpprieto commented Dec 6, 2023

Hydrogen example: Partytown + Google Tag Manager + CSP

This folder contains a peformance-oriented example lazy-loading Google Tag Manager
using Partytown.

Party town helps relocate resource intensive scripts into a web worker, and off of the main thread.
Its goal is to help speed up sites by dedicating the main thread to your code, and offloading third-party scripts to a web worker.

Requirements

  • [Google Tag Manager ID] - Log in to your Google Tag Manager account and open a container. In the top right corner (next to the Submit and Preview buttons) you'll see some short text that starts with GTM- and then contains some letters/numbers. That's your Google Tag Manager ID
  • Basic Partytown knowledge - Introducing Partytown: Run Third-Party Scripts From a Web Worker

Key files

This folder contains the minimal set of files needed to showcase the implementation.
Files that aren’t included by default with Hydrogen and that you’ll need to
create are labeled with 🆕.

File Description
🆕 .env.example Example environment variable file. Copy the relevant variables to your existing .env file, if you have one.
🆕 app/components/PartytownGoogleTagManager.tsx A component that loads Google Tag Manager in a web worker via Partytown with built-in CSP support.
🆕 app/utils/partytown/maybeProxyRequest.ts A Partytown url resolver to control which 3P scripts should be reverse-proxied. Used in Partytown's resolveUrl property
🆕 app/utils/partytown/partytownAtomicHeaders.ts Utility that returns the required headers to enable Atomics mode for added performance
🆕 app/routes/reverse-proxy.ts A route that acts as a reverse proxy for 3P scripts that require CORS headers
app/root.tsx The root layout where Partytown and GTM is implemented
app/routes/_index.tsx The home route where a GTM pageView event is emmited
app/entry.server.tsx Add GTM domain to the script-src directive

Dependencies

Module Description
🆕 @builder.io/partytown Partytown is a lazy-loaded library to help relocate resource intensive scripts into a web worker, and off of the main thread. Its goal is to help speed up sites by dedicating the main thread to your code, and offloading third-party scripts to a web worker.

Instructions

1. Install required dependencies

npm i @builder.io/partytown

2. Modify npm scripts

In package.json modify the build script and add the partytown script

Add the partytown script which copies the library files to /public

  "scripts": {
+    "partytown": "partytown copylib public/~partytown"
  },

Modify the build script to copy the partytown library files to /public before every build

  "scripts": {
-    "build": "shopify hydrogen build",
+    "build": "npm run partytown && shopify hydrogen build",
  },

View the complete component file to see these updates in context.

3. Copy the library files

npm run partytown

4. Copy over the new files

  • In your Hydrogen app, create the new files from the file list above, copying in the code as you go.
  • If you already have a .env file, copy over these key-value pairs:
    • GTM_CONTAINER_ID - To obtain your GTM container ID follow these instructions

5. Edit the root.tsx layout file

Import the required components and utilties

import {Partytown} from '@builder.io/partytown/react';
import {PartytownGoogleTagManager} from '~/components/PartytownGoogleTagManager';
import {maybeProxyRequest} from '~/utils/partytown/maybeProxyRequest';
import {partytownAtomicHeaders} from '~/utils/partytown/partytownAtomicHeaders';

Update the loader function

export async function loader({context}: LoaderFunctionArgs) {
  const layout = await context.storefront.query<{shop: Shop}>(LAYOUT_QUERY);
  return json(
    {
      layout,
      // 1. Pass the GTM container ID to the client
      gtmContainerId: context.env.GTM_CONTAINER_ID,
    },
    {
      // 2. Enable atomic mode
      headers: partytownAtomicHeaders(),
    },
  );
}

Update the App component

export default function App() {
  // 1. Retrieve the GTM container ID
  const {gtmContainerId} = useLoaderData<typeof loader>();
  const nonce = useNonce();

  return (
    <html lang="en">
      <head>
        <Meta />
        <Links />
      </head>

      <body>
        <Outlet />
        <ScrollRestoration nonce={nonce} />
        <Scripts nonce={nonce} />

        {/* 2. Initialize the GTM dataLayer container */}
        <Script
          type="text/partytown"
          dangerouslySetInnerHTML={{
            __html: `
              dataLayer = window.dataLayer || [];

              window.gtag = function () {
                dataLayer.push(arguments);
              };

              window.gtag('js', new Date());
              window.gtag('config', "${gtmContainerId}");
            `,
          }}
        />

        {/* 3. Include the GTM component */}
        <PartytownGoogleTagManager gtmContainerId={gtmContainerId} />

        {/* 4. Initialize PartyTown */}
        <Partytown
          nonce={nonce}
          forward={['dataLayer.push', 'gtag']}
          resolveUrl={maybeProxyRequest}
        />
      </body>
    </html>
  );
}

View the complete component file to see these updates in context.

6. (Optional) - Update Content Securirt Policy

Add wwww.googletagmanager.com domain to the scriptSrc directive

//...other code

export default async function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext,
) {
- const {nonce, header, NonceProvider} = createContentSecurityPolicy();
+  const {nonce, header, NonceProvider} = createContentSecurityPolicy({
+    scriptSrc: ["'self'", 'cdn.shopify.com', 'www.googletagmanager.com'],
+ });

  //...other code

  responseHeaders.set('Content-Security-Policy', header);

  return new Response(body, {
    headers: responseHeaders,
    status: responseStatusCode,
  });
}

View the complete component file to see these updates in context.

7. (TypeScript only) - Add the new environment variable to the ENV type definition

Update the remix.d.ts file

// ...other code

declare global {
  /**
   * A global `process` object is only available during build to access NODE_ENV.
   */
  const process: {env: {NODE_ENV: 'production' | 'development'}};

  /**
   * Declare expected Env parameter in fetch handler.
   */
  interface Env {
    SESSION_SECRET: string;
    PUBLIC_STOREFRONT_API_TOKEN: string;
    PRIVATE_STOREFRONT_API_TOKEN: string;
    PUBLIC_STORE_DOMAIN: string;
    PUBLIC_STOREFRONT_ID: string;
+   GTM_CONTAINER_ID: `GTM-${string}`;
  }
}

// ...other code

View the complete component file to see these updates in context.

@juanpprieto juanpprieto force-pushed the juanpprieto/example-partytown branch from e348e86 to 92c862b Compare December 6, 2023 00:47
@juanpprieto juanpprieto requested a review from a team December 6, 2023 00:49
@juanpprieto juanpprieto force-pushed the juanpprieto/example-partytown branch from 92c862b to 6a2fc95 Compare December 6, 2023 00:56
examples/partytown/CHANGELOG.md Outdated Show resolved Hide resolved
examples/partytown/README.md Outdated Show resolved Hide resolved
examples/partytown/README.md Outdated Show resolved Hide resolved
examples/partytown/README.md Outdated Show resolved Hide resolved
examples/partytown/README.md Outdated Show resolved Hide resolved
@github-actions github-actions bot had a problem deploying to preview December 6, 2023 15:29 Failure
@github-actions github-actions bot had a problem deploying to preview December 6, 2023 15:29 Failure
@github-actions github-actions bot had a problem deploying to preview December 6, 2023 15:30 Failure
@github-actions github-actions bot had a problem deploying to preview December 7, 2023 19:58 Failure
@github-actions github-actions bot had a problem deploying to preview January 23, 2024 18:28 Failure
@github-actions github-actions bot had a problem deploying to preview January 24, 2024 22:58 Failure
@juanpprieto juanpprieto mentioned this pull request Jan 25, 2024
@juanpprieto
Copy link
Contributor Author

Replaced with #1683

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants