BuilderIO / partytown

Relocate resource intensive third-party scripts off of the main thread and into a web worker. 🎉
https://partytown.builder.io
MIT License
12.85k stars 426 forks source link

Support for Google Tag Assistant (Google Tag Manager preview) #72

Open Manduro opened 2 years ago

Manduro commented 2 years ago

When using Google Tag Assistant to test a Proxytown implementation of Google Tag Manager, it fails to connect: Screenshot 2022-02-11 at 16 01 04 At first there were some CORS errors for the googletagmanager.com domain. After proxying those I can see the iframe getting added to the page, but it fails to load and some Partytown error occurs:

Screenshot 2022-02-11 at 16 07 01

In the page's <head> I can see the following scripts:

<script type="text/partytown-x" src="http://localhost:3003/~partytownproxy/google-analytics/analytics.js" data-ptsrc="https://www.google-analytics.com/analytics.js" data-ptid="351043767"></script>
<script type="text/partytown-x" src="http://localhost:3003/~partytownproxy/googletagmanager/gtag/js" data-ptsrc="http://www.googletagmanager.com/gtag/js?id=G-44K8TKMTS9&amp;l=dataLayer&amp;cx=c" data-ptid="574473788"></script>
<script type="text/partytown" src="http://localhost:3003/~partytownproxy/googletagmanager/debug/bootstrap" data-ptsrc="https://www.googletagmanager.com/debug/bootstrap?id=GTM-XXXXXXX&amp;src=GTM&amp;cond=2&amp;gtm=2wg290" data-ptid="433709864" data-pterror="Error: Error finding instance &quot;1&quot; on window 2 (259405394)
    at sendToMain (http://localhost:3003/~partytown/debug/partytown-ww-sw.js:253:27)
    at queue (http://localhost:3003/~partytown/debug/partytown-ww-sw.js:226:20)
    at callMethod (http://localhost:3003/~partytown/debug/partytown-ww-sw.js:333:20)
    at Node.<anonymous> (http://localhost:3003/~partytown/debug/partytown-ww-sw.js:1338:24)
    at wc (https://www.googletagmanager.com/debug/bootstrap?id=GTM-XXXXXXX&amp;src=GTM&amp;cond=2&amp;gtm=2wg290:53:163)
    at eval (https://www.googletagmanager.com/debug/bootstrap?id=GTM-XXXXXXX&amp;src=GTM&amp;cond=2&amp;gtm=2wg290:66:331)
    at lc (https://www.googletagmanager.com/debug/bootstrap?id=GTM-XXXXXXX&amp;src=GTM&amp;cond=2&amp;gtm=2wg290:47:924)
    at Bc.start (https://www.googletagmanager.com/debug/bootstrap?id=GTM-XXXXXXX&amp;src=GTM&amp;cond=2&amp;gtm=2wg290:65:932)
    at eval (https://www.googletagmanager.com/debug/bootstrap?id=GTM-XXXXXXX&amp;src=GTM&amp;cond=2&amp;gtm=2wg290:70:1364)
    at Proxy.eval (https://www.googletagmanager.com/debug/bootstrap?id=GTM-XXXXXXX&amp;src=GTM&amp;cond=2&amp;gtm=2wg290:77:3)"></script>

And just before the </body>:

<iframe src="about:blank" class="__TAG_ASSISTANT_BADGE" frameborder="0" style="border: 0 !important; bottom: 24px !important; border-radius: 8px !important; box-shadow: 0 3px 5px -1px rgb(0 0 0 / 20%), 0 5px 8px 0 rgb(0 0 0 / 14%), 0 1px 14px 0 rgb(0 0 0 / 12%) !important; clip: initial !important; display: inline !important; height: 200px !important; left: auto !important; margin: 0 !important; max-width: 95% !important; opacity: 1 !important; padding: 0 !important; position: fixed !important; right: 24px !important; top: auto !important; visibility: visible !important; width: 450px !important; z-index: 2147483647 !important;" data-ptwindow="259405394"></iframe>

When switching back to the default GTM implementation (without Partytown), everything works as expected.

ghost commented 2 years ago

I am having the same issue here. On top of that, I also seem to be getting CORs for google analytics. image

Legym commented 2 years ago

I am also loading Google Tag Assistant. However by sheer luck it randomly connected. I kept refreshing and after about 40 times it finally connected. Ive been able to connect 3 times without the error, but it does seem like its completely random. I've been getting OPs original error when it does not connect.

image

image

ghost commented 2 years ago

So the main reason this is happening from my investigation is that when you try to connect google tag assistant to your app, it will try to fetch the script from https://www.googletagmanager.com/debug/bootstrap?cond=2&id=GTM-xxxxxxx which does not have the right headers in place and results in CORs.

This requires a reverse proxy set up for this particular resource and I've set it up a way as shown below:

                <Partytown
                    forward={['dataLayer.push']}
                    resolveUrl={(url) => {
                        if (url.pathname.includes('debug/bootstrap')) {
                            var proxyUrl = new URL(
                                'https://your-hosted-url/party-proxy',
                            )
                            proxyUrl.searchParams.append('url', url)
                            return proxyUrl
                        }
                        return url
                    }}
                />

and I've set up a proxy with NGINX with a hard-coded link for testing purposes as below

    location /party-proxy {
        proxy_set_header Access-Control-Allow-Origin *;
        proxy_pass https://www.googletagmanager.com/debug/bootstrap?id=GTM-xxxxxxx&src=GTM&cond=2;
    }

This helps me get passed the CORs issue but I end up with these errors in the console after the timeout and I can't seem to identify what I may need to forward to the main thread.

image

image

tarponjargon commented 2 years ago

This helps me get passed the CORs issue but I end up with these errors in the console after the timeout and I can't seem to identify what I may need to forward to the main thread.

Yeah I got to the same point. the c.Postmessage is not a function error is repeated every second.

To make sure it's not a disallowing the POST method I tried adding:

headers["Access-Control-Allow-Origin"] = "*"; headers["Access-Control-Allow-Methods"] = "GET,POST,HEAD,OPTIONS";

To my reverse proxy to https://www.googletagmanager.com/debug/bootstrap... And that doesn't seem to be the issue.

There's a partytown JS error that precedes the postMessage error. I ran partytown in debug mode and here's the console output (in case it's helpful to anybody).

The solution I ultimately arrived at is to set a query param like ?no_partytown=1 that if true, suppresses type="text/partytown" on the gtm script tag.

Screen Shot 2022-03-17 at 3 54 56 PM

slawekkolodziej commented 2 years ago

I played with integrating tag assistant here: https://github.com/slawekkolodziej/partytown/tree/tag-assistant

I wrote a document with what I've found: https://github.com/slawekkolodziej/partytown/blob/tag-assistant/src/lib/web-worker/tag-assistant.md

The implementation is not finished yet. It's rather at early stage / POC. There are some missing parts and the implementation is definitely not production ready.

graysonhicks commented 2 years ago

Thanks @slawekkolodziej, I think the doc you linked has moved here: https://github.com/slawekkolodziej/partytown/blob/tag-assistant/docs/tag-assistant.md.

Would this issue be causing this behavior?

image image

Basically added GTM with Partytown but unable to debug that events are working before merging Partytown to production.

slawekkolodziej commented 2 years ago

Hey @graysonhicks yes, I moved that file recently. From your screenshots I can tell that you are using legacy Tag Assistant plugin. My code works with Tag Assistant Companion (https://chrome.google.com/webstore/detail/tag-assistant-companion/jmekfmbnaedfebfnmakmokmlfpblbfdm).

I figured out a nicer way to integrate Tag Assistant that doesn't require adding GTM-specific code to Partytown. However, it still needs some changes in src/lib/web-worker/worker-serialization.ts.

ryfazrin commented 2 years ago

thanks @slawekkolodziej, I found a similar problem in version 6.2. I think you have added this from your PR (https://github.com/BuilderIO/partytown/pull/183).

Do I need to follow the steps you share here https://github.com/slawekkolodziej/partytown/blob/tag-assistant/docs/tag-assistant.md ?

Callan003 commented 2 years ago

I am facing the same issue. I am able to move the GTM script to a SW, but once it is there, I am not able to test the GTM events.

Any suggestion on how I should test it?

franceschiniandrea commented 1 year ago

I read the guide about debugging Tag Assistant, but I'm still confused on a few things:

  1. How do I check whether GTM is running in debug mode?
  2. How do I proxy the request in Gatsby? Do I necessarily need a backend?
graysonhicks commented 1 year ago

@NotFrancee I'm still not clear either on the debug mode.

For Gatsby the proxy is either with createRedirect if you are not using the Script component or with the partytownProxiedUrls option if you are using the Script component.

Without Script component: https://github.com/PaulieScanlon/gatsby-third-party-scripts/pull/4/files

With Script component: https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-script/#proxy-configuration

babvi commented 1 year ago

I'm also facing the same error partytown-ww-sw.js?v=0.7.3:1715 Error: Error finding instance "1ycwlf6dz4.d" on window 2

On nextJS 11. Anyone know how to test / fix this?

babvi commented 1 year ago

The solution I ultimately arrived at is to set a query param like ?no_partytown=1 that if true, suppresses type="text/partytown" on the gtm script tag.

@tarponjargon how you fixed it? Can you paste is here?

slawekkolodziej commented 1 year ago

Just curious, are you trying to use this within react application? I may wrap the code I created in a react component sometime over the next week or two and release it as npm module.

gabrielmlinassi commented 1 year ago

@slawekkolodziej if you have a sample of how to use partytown with NextJS, please I'd love to see one. I've tried a lot and never managed to make it work with NextJS Script component worker strategy. It always throws CORS errors. Partytown docs says to use NextJS rewrites as a reverse-proxy but never really understood how to do it. Never found any example either.

babvi commented 1 year ago

@slawekkolodziej I'm working with Next 11 and able to load all GTM and other 3rd parties which are added inside GTM with worker. I'm able to see realtime analysis in GA as well with partytown worker, But when doing "Tag Assistant" check its show instance error . I did added all proxies of all my third-parties on nginx which works good.

LynxTR commented 1 year ago

@babvi If its possible can you share a minimal repo. I am facing same issue and couldnt solve it for few days...

phongnguyen0601 commented 1 year ago

I have faced same issue. But I just resolved this issue by update partytown version from 0.7.1 to 0.7.4 and add below code into Set Hook to disable partytown when using Google Tag Assistant (check debug signal in the URL - gtm_debug) in NextJS Static Web:

set={(opts) => { const isDebugging = opts.window.location.search.includes("gtm_debug"); if ( isDebugging && opts.name === "type" && opts.nodeName === "SCRIPT" ) { return opts.prevent; } return opts.continue; }}

StoraH commented 1 year ago

@phongnguyen0601 We came up with a similar solution for our SSR (not using NEXT) and it's an okay workaround for the moment.

 function checkIfGoogleTagAssistantIsEnabled(ctx) {
    const [, query] = ctx.request.url.split('?');
    const searchParams = new URLSearchParams(query);

    return !!searchParams.get('gtm_debug');
}

return `<script type="text/${
        checkIfGoogleTagAssistantIsEnabled(ctx) ? 'javascript' : 'partytown'
    }">(function(w,d,s,l,i){w[l]=w[l]||[];w[l].p..........
SujayPrabhu96 commented 1 year ago

This is how I fixed the CORS error for analytics and preview mode

Use resolveUrl in gatsby-ssr.js:

<script
    key="partytown-vanilla-config"
    dangerouslySetInnerHTML={{
       __html: `partytown = {
          resolveUrl(url, location) {
           if (
            url.hostname === "www.google-analytics.com" &&
            url.pathname.endsWith(".js")
           ) {
             var proxyUrl = new URL(location.origin+'/google-analytics');
             proxyUrl.searchParams.append("url", url.href);
             return proxyUrl;
          }
          if (
            url.pathname.includes("/debug/bootstrap")
          ) {
            var proxyUrl = new URL(location.origin+'/googletagmanager/debug/bootstrap');
            proxyUrl.searchParams.append("url", url.href);
            return proxyUrl;
          }
          return url;
         }
       }`,
    }}
 />,

createRedirect in gatsby-node.js

exports.onPreBuild = async ({ actions: { createRedirect } }: any) => {
  await copyLibFiles(path.join(__dirname, "static", "~partytown"));

  createRedirect({
    fromPath: `/google-analytics?url=:url`,
    toPath: `:url`,
    statusCode: 200,
  });

  createRedirect({
    fromPath: `/googletagmanager/debug/bootstrap?url=:url`,
    toPath: `:url`,
    statusCode: 200,
  });
};
NavidGoalpure commented 1 year ago

@slawekkolodziej if you have a sample of how to use partytown with NextJS, please I'd love to see one. I've tried a lot and never managed to make it work with NextJS Script component worker strategy. It always throws CORS errors. Partytown docs says to use NextJS rewrites as a reverse-proxy but never really understood how to do it. Never found any example either.

If you are looking for a solution, you can take a look at the configuration in my last project. you must put related codes to _app and _document

https://github.com/NavidGoalpure/visabox/blob/master/pages/_document.tsx https://github.com/NavidGoalpure/visabox/blob/master/pages/_app.tsx

I found this solution on the Internet of course, but I can remember the reference.

NavidGoalpure commented 1 year ago

I have faced same issue. But I just resolved this issue by update partytown version from 0.7.1 to 0.7.4 and add below code into Set Hook to disable partytown when using Google Tag Assistant (check debug signal in the URL - gtm_debug) in NextJS Static Web:

set={(opts) => { const isDebugging = opts.window.location.search.includes("gtm_debug"); if ( isDebugging && opts.name === "type" && opts.nodeName === "SCRIPT" ) { return opts.prevent; } return opts.continue; }}

It works for me! thanks

phongnguyen0601 commented 1 year ago

I have faced same issue. But I just resolved this issue by update partytown version from 0.7.1 to 0.7.4 and add below code into Set Hook to disable partytown when using Google Tag Assistant (check debug signal in the URL - gtm_debug) in NextJS Static Web:

set={(opts) => { const isDebugging = opts.window.location.search.includes("gtm_debug"); if ( isDebugging && opts.name === "type" && opts.nodeName === "SCRIPT" ) { return opts.prevent; } return opts.continue; }}

You can try another way to disable partytown by checking attribute data-tag-assistant-present of document element (#document) inside iframe of partytown sandbox service worker. It works for me:

let docElement = opts.window.document.documentElement;
let assistant = docElement.getAttribute("data-tag-assistant-present");

if (assistant && opts.name === "type" && opts.nodeName === "SCRIPT") {
     return opts.prevent;
}
carlosananias commented 1 year ago

@phongnguyen0601 We came up with a similar solution for our SSR (not using NEXT) and it's an okay workaround for the moment.

 function checkIfGoogleTagAssistantIsEnabled(ctx) {
  const [, query] = ctx.request.url.split('?');
  const searchParams = new URLSearchParams(query);

  return !!searchParams.get('gtm_debug');
}

return `<script type="text/${
      checkIfGoogleTagAssistantIsEnabled(ctx) ? 'javascript' : 'partytown'
  }">(function(w,d,s,l,i){w[l]=w[l]||[];w[l].p..........

this is great!! very useful!! thanks @phongnguyen0601

excelsior091224 commented 1 year ago

I have same issue. I'm using @astrojs/partytown.

image image
multiplehats commented 1 year ago

Here's how I'm solving this in SvelteKit (not the most dry, but you barely touch this anyway).

//+layout.server.ts
import type { LayoutServerLoad } from './$types';

export const load = (async (event) => {
    const { url } = event;
    const gtmDebug = url.searchParams.get('gtm_debug');

    return {
        gtmDebug: !!gtmDebug
    };
}) satisfies LayoutServerLoad;
<script lang="ts">
    import type { LayoutData } from './$types';
    import { partytownSnippet } from '@builder.io/partytown/integration';
    import { onMount } from 'svelte';
    import { VERCEL_PROD } from '$lib/shared/utils/environment';

    export let data: LayoutData;
    $: ({ gtmDebug } = data);

    let scriptEl: HTMLScriptElement | null = null;

    onMount(() => {
        if (scriptEl) {
            scriptEl.textContent = partytownSnippet();
        }
    });
</script>

<svelte:head>
    {#if VERCEL_PROD}
        <script>
            partytown = {
                forward: ['dataLayer.push'],
                resolveUrl: (url) => {
                    const siteUrl = 'https://nocode.gallery/fissa';
                    if (url.hostname === 'www.googletagmanager.com') {
                        const proxyUrl = new URL(`${siteUrl}/gtm`);

                        const gtmId = new URL(url).searchParams.get('id');
                        gtmId && proxyUrl.searchParams.append('id', gtmId);

                        return proxyUrl;
                    } else if (url.hostname === 'www.google-analytics.com') {
                        const proxyUrl = new URL(`${siteUrl}/ga`);

                        return proxyUrl;
                    }
                    return url;
                }
            };
        </script>

        <script bind:this={scriptEl}></script>

        {#if gtmDebug}
            <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-XXX');
            </script>
        {:else}
            <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-XXX');
            </script>
        {/if}
    {/if}
</svelte:head>

<slot />
grahamd711 commented 1 year ago

Just curious, are you trying to use this within react application? I may wrap the code I created in a react component sometime over the next week or two and release it as npm module.

@slawekkolodziej thanks for your great work on exploring implementation of Tag Assistant! Could you please share the implementation you used for GTM beyond the example? we tried the approach here in our GatsbyJS project, but could not get native page views to fire within GTM: https://github.com/slawekkolodziej/partytown/blob/tag-assistant/docs/tag-assistant.md. We are using onRouteChange and sending custom events to GTM to fire tags in preview.

An NPM module would be a huge win for the Gatsby + Partytown side!

slawekkolodziej commented 1 year ago

@grahamd711, sorry for not responding sooner, I've been super busy lately, will try to wrap this up during the first half of May.

grahamd711 commented 1 year ago

@slawekkolodziej sounds great! I will keep you updated on my progress with GTM events as well

SujayPrabhu96 commented 1 year ago

@slawekkolodziej Hey man, do have any updates? 😬

slawekkolodziej commented 1 year ago

I worked on this more during this week, hoping to have an npm package next week 👍

grahamd711 commented 1 year ago

@slawekkolodziej looking forward to testing! I can deploy on two production sites rather quickly to get some early feedback for you

korzh875 commented 1 year ago

Pure html implementation

index.html

<head>
  <script>
          function checkIfGoogleTagAssistantIsEnabled() {
              const urlParams = new URLSearchParams(window.location.search);
              const gtm_debug = urlParams.get('gtm_debug');

              return !!gtm_debug;
          }

          document.addEventListener('DOMContentLoaded', function() {
              var scriptType = checkIfGoogleTagAssistantIsEnabled() ? 'text/javascript' : 'text/partytown';
              var script = document.createElement('script');
              script.type = scriptType;
              script.async = true;
              script.src = 'https://www.googletagmanager.com/gtm.js?id=GTM-XXXX';
              var firstScript = document.getElementsByTagName('script')[0];
              firstScript.parentNode.insertBefore(script, firstScript);
          });
  </script>
  <script type="text/javascript" src="/~partytown/partytown.js">
  <script>
          partytown = {
              forward: ['dataLayer.push'],
          };
  </script>
  <script type="text/partytown" src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXX"></script>
  <script type="text/partytown">
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());

          gtag('config', 'G-XXXXX');
  </script>
<head>
webpack.config.js
  ...
  new CopyPlugin({
                  patterns: [
                      {
                          from: partytown.libDirPath(),
                          to: path.join(__dirname, 'public', '~partytown'),
                      },
                  ],
              }),
  ...
slawekkolodziej commented 1 year ago

I worked on this more during this week, hoping to have an npm package next week 👍

Or next month 😅


I have finally published the npm package for GTM integration. You can find it on npm at @superside-oss/partytown-gtm.

Before you start using the package, please take note of the following:

  1. The package requires the Tag Assistant Companion browser plugin to work properly.
  2. I apologize for not having a README file ready at the moment, but I will make sure to create one soon.
  3. Please be aware that the module is currently in the prerelease stage, use it at your own risk.

I have included a sample Next.js integration. In this integration, there are two key files that you should focus on:

grahamd711 commented 1 year ago

amazing work @slawekkolodziej this is great 🤝 ! I'll get this wired up in our nextjs app and let you know how it goes.

phunguyen-ct commented 1 year ago

I worked on this more during this week, hoping to have an npm package next week 👍

Or next month 😅

I have finally published the npm package for GTM integration. You can find it on npm at @superside-oss/partytown-gtm.

Before you start using the package, please take note of the following:

  1. The package requires the Tag Assistant Companion browser plugin to work properly.
  2. I apologize for not having a README file ready at the moment, but I will make sure to create one soon.
  3. Please be aware that the module is currently in the prerelease stage, use it at your own risk.

I have included a sample Next.js integration. In this integration, there are two key files that you should focus on:

  • src/components/ThirdParty.tsx: This file imports the GTMScript component, which takes care of loading GTM and adding the necessary snippets for Partytown integration.
  • src/app/api/third-party/route.ts: Some files cannot be loaded by Partytown due to missing CORS headers. This issue affects certain Tag Assistant files as well. To overcome the problem, this API endpoint creates a proxy for problematic files.

hi @slawekkolodziej, I have integrated partytown to my next.js app follow your example, my web app run based on next.js 12, react 17. It is working perfectly but got the issue while enable debugging with GTM extension -> (Tag not found) with gtm debug. Could you help? Thank you in advance!

Screen Shot 2023-06-21 at 14 58 32
fprl commented 11 months ago

@excelsior091224

I have same issue. I'm using [@astrojs/partytown]

To anyone using Astro or something similar:

  1. Create a gtm.astro component

    ---
    ---
    <script>
    const searchParams = new URL(window.location.href);
    const isDebug = searchParams.searchParams.get('gtm_debug');
    
    const scripts = [
        {
            src: 'https://www.googletagmanager.com/gtm.js?id=google-tag-id',
            text: null,
        },
        {
            src: null,
            text: `
      window.dataLayer = window.dataLayer || [];
    function gtag() {
      dataLayer.push(arguments);
    }
    
    gtag('js', new Date());
    gtag('config', 'GA_MEASUREMENT_ID');
      `,
        },
    ];
    
    function injectScripts() {
        scripts.forEach((s) => {
            const script = document.createElement('script');
            script.type = isDebug ? 'text/javascript' : 'text/partytown';
    
            if (s.src) script.src = s.src;
            if (s.text) script.text = s.text;
            document.head.appendChild(script);
        });
    }
    
    injectScripts();
    </script>
  2. proxy the requests in astro.config.mjs

    
    partytown({
    config: {
    resolveUrl: (url, location) => {
      // these were the proxied hosts I wanted to proxy
      const proxiedHosts = [
        'googletagmanager.com',
        'connect.facebook.net',
        'googleads.g.doubleclick.net',
      ]
    
      if (proxiedHosts.includes(url.hostname)) {
        const proxyUrl = new URL('proxy path', 'proxy origin');
        proxyUrl.searchParams.append('url', url.href);
        return proxyUrl;
      }
    
      return url;
    },
    forward: ['dataLayer.push', 'fbq'],
    },
    }),
    ``

You can then implement an API endpoint that proxies a request to those proxiedHosts or whatever you need.

geekysaurabh001 commented 11 months ago

So, anyways to fix the CORS issue and other issue in NextJS Pages Directory version 12-13?

arulpr commented 11 months ago

@excelsior091224

I have same issue. I'm using [@astrojs/partytown]

To anyone using Astro or something similar:

1. Create a `gtm.astro` component
---
---
<script>
  const searchParams = new URL(window.location.href);
  const isDebug = searchParams.searchParams.get('gtm_debug');

  const scripts = [
      {
          src: 'https://www.googletagmanager.com/gtm.js?id=GTM-PCMP3LG',
          text: null,
      },
      {
          src: null,
          text: `
      window.dataLayer = window.dataLayer || [];
    function gtag() {
      dataLayer.push(arguments);
    }

    gtag('js', new Date());
    gtag('config', 'GA_MEASUREMENT_ID');
      `,
      },
  ];

  function injectScripts() {
      scripts.forEach((s) => {
          const script = document.createElement('script');
          script.type = isDebug ? 'text/javascript' : 'text/partytown';

          if (s.src) script.src = s.src;
          if (s.text) script.text = s.text;
          document.head.appendChild(script);
      });
  }

  injectScripts();
</script>
2. proxy the requests in `astro.config.mjs`
partytown({
  config: {
    resolveUrl: (url, location) => {
      // these were the proxied hosts I wanted to proxy
      const proxiedHosts = [
        'googletagmanager.com',
        'connect.facebook.net',
        'googleads.g.doubleclick.net',
      ]

      if (proxiedHosts.includes(url.hostname)) {
        const proxyUrl = new URL('proxy path', 'proxy origin');
        proxyUrl.searchParams.append('url', url.href);
        return proxyUrl;
      }

      return url;
    },
    forward: ['dataLayer.push', 'fbq'],
  },
}),
``

You can then implement an API endpoint that proxies a request to those `proxiedHosts` or whatever you need.

Thank you this worked.

excelsior091224 commented 11 months ago

@fprl

It works. Thanks!

<!-- New GTM Test 2 -->
<script>
  const searchParams = new URL(window.location.href);
  const isDebug = searchParams.searchParams.get("gtm_debug");

  const scripts = [
    {
      src: "https://www.googletagmanager.com/gtm.js?id=MY-GTM-ID",
      text: null,
    },
    {
      src: null,
      text: `
      window.dataLayer = window.dataLayer || [];
    function gtag() {
      dataLayer.push(arguments);
    }

    gtag('js', new Date());
    gtag('config', 'MY-ANALYTICS-ID');
      `,
    },
  ];

  function injectScripts() {
    scripts.forEach((s) => {
      const script = document.createElement("script");
      script.type = isDebug ? "text/javascript" : "text/partytown";

      if (s.src) script.src = s.src;
      if (s.text) script.text = s.text;
      document.head.appendChild(script);
    });
  }

  injectScripts();
</script>
    partytown({
      // Adds dataLayer.push as a forwarding-event.
      config: {
        resolveUrl: (url, location) => {
          // these were the proxied hosts I wanted to proxy
          const proxiedHosts = [
            "googletagmanager.com",
            "connect.facebook.net",
            "googleads.g.doubleclick.net",
          ];

          if (proxiedHosts.includes(url.hostname)) {
            const proxyUrl = new URL("proxy path", "proxy origin");
            proxyUrl.searchParams.append("url", url.href);
            return proxyUrl;
          }

          return url;
        },
        forward: ["dataLayer.push", "fbq"],
        // forward: ["dataLayer.push"],
      },
    }),
j-vee commented 10 months ago

Thanks for the tips everyone, we've gone with the same solution for now where partytown is disabled when used with Tag assistant. Hoping to see an update to the library to remove that need.

RenuSuresh commented 10 months ago

@slawekkolodziej I have tried the same that you have shared, @superside-oss/partytown-gtm. to verify I have replaced my GTM-ID with the next-js implementation, but I am getting below error Could you please help to check?

image
slawekkolodziej commented 10 months ago

It seems that the error is in your script. You can see match_utm_source is not a function. Does it work if you run it with just text/partytown and not through <GTMScript>?

RenuSuresh commented 10 months ago

@slawekkolodziej I have tried with both text/partytown and <GTMScript> but getting same error. However, If I use standard way of GTM script, it does not give any error. Could you please tell why this might be happening?

geekysaurabh001 commented 10 months ago

This PartyTown doesn't seem to work in NextJS. Also, no proper documentation to showcase the errors and solve them either. CORS issue is not as straightforward to solve in the NextJS it seems and even those who managed to solve it are getting some other errors.

slawekkolodziej commented 10 months ago

@RenuSuresh seems like one of your third-party scripts is not working well in Partytown. This feels unrelated to the GTM. You can debug this by directly importing these scripts into Partytown and disabling them one at a time to isolate the one causing errors. Once you know which one is making troubles, you can try to fix it yourself or create a new Partytown issue.

@geekysaurabh001 are you talking about Partytown in general or the GTM plugin specifically? I've had Partytown and the GTM plugin running smoothly on a production NextJS app for over a year. The recently added README for @superside-oss/partytown-gtm includes setup instructions now. However it has always featured an example NextJS app. This is open source, if you feel that something is missing, feel free to contribute via pull request. 😄

geekysaurabh001 commented 10 months ago

@RenuSuresh seems like one of your third-party scripts is not working well in Partytown. This feels unrelated to the GTM. You can debug this by directly importing these scripts into Partytown and disabling them one at a time to isolate the one causing errors. Once you know which one is making troubles, you can try to fix it yourself or create a new Partytown issue.

@geekysaurabh001 are you talking about Partytown in general or the GTM plugin specifically? I've had Partytown and the GTM plugin running smoothly on a production NextJS app for over a year. The recently added README for @superside-oss/partytown-gtm includes setup instructions now. However it has always featured an example NextJS app. This is open source, if you feel that something is missing, feel free to contribute via pull request. 😄

Yes @slawekkolodziej I am trying that but failing miserably 😂 I'll contribute my solution once I figure it out. I was talking about PartyTown in general though. I am on NextJS 12 and I am unable to figure out an approach that would work.

  1. I follow the doc of Partytown NextJS.
  2. Then I go with the experimental worker strategy.
  3. But it gives CORS error in the browser console.

I am basically stuck at that party. In the GTM preview mode, I also cannot connect.

What steps did you follow to setup partytown with nextjs, if you can let me know, it will be helpful 🙏

slawekkolodziej commented 10 months ago

Oh, I see, so you're using NextJS built-in Script component with strategy="worker". I never tried that really. I added Partytown directly to my app. Regarding CORS errors - most likely third party services you're trying to use do not set proper CORS headers and for that reason have to be proxied (you can learn more about this in Partytown docs: https://partytown.builder.io/proxying-requests)

geekysaurabh001 commented 10 months ago

Yes @slawekkolodziej I am trying to understand how to setup proxy in nextjs for this but cannot figure it out till now.

slawekkolodziej commented 10 months ago

There's an example proxy endpoint in the GTM plugin NextJS integration: https://github.com/superside-oss/partytown-gtm-plugin/blob/alpha/packages/integration/nextjs/src/app/api/third-party/route.ts