partytown
partytown copied to clipboard
Google ads dont work
Google ads not working if loaded through partytown but other plugin work. Why?
I want to know how i can add Google Adsense script in my project with partytown.
Do u also know how can I remove partytown console.log error from my production?
You can select which scripts should not be loaded by partytown.
See https://partytown.builder.io/configuration loadScriptsOnMainThread
I can confirm that Google Ads does not work with partytown. If you add Google ads tracking through gtag of GTM, it has to load a script:
https://googleads.g.doubleclick.net/pagead/viewthroughconversion/* , which redirects it to https://www.google.com/pagead/1p-conversion/XYZ/ then finally to https://www.google.com/pagead/1p-user-list/XYZ/
These redirects load every needed tracking cookie for it to work, however, since only the first URL can be resolved, it can not go through the chain. Even if I do a reverse proxy through CloudFront on the first googleads.g.doubleclick.net url, it will give CORS error on the second one.
I can not use loadScriptsOnMainThread either, because the script gets loaded by gtag/GTM.
I am not sure this is even possible to solve with partytown as it is now.
@vptill doesn't loadScriptsOnMainThread
handles any script loaded by partytown? While Adsense gets loaded by GTM, it is still being appended via script tag and loaded by partytown in the end. The problem is that the url search params appears to be variable, and can't be added to the loadScriptsOnMainThread
list ahead of time, as partytown assumes strict equality of urls. But you could solve it by disabling loading Adsens via GTM and load it manually instead
@andreyvolokitin I use gtag with Google Analytics and Ads tracking. That means, once I load GA, it will load the necessary scripts for tracking Ads as well, because the two are connected in GA4 and that is the recommended usage to track conversions in Google Ads. Separating the two is not really an option for me, the only solution would be to have reverse proxy on all the urls (https://googleads.g.doubleclick.net, google.com), not only the first one.
Tried both using ads through GTM and integrating directly and neither worked. Sad to see that google ads can't work with partytown.
You can use server side tracking for that. We're going to utilize that.
Saves bandwidth and works with partytown
Creating a separate layout for your conversion tracking page, which loads the scripts natively and using partytown on the other layouts, works as well.
I got google ads loaded via GTM to work using cloudfront proxy to googleads.g.doubleclick.net.
I believe the "AllViewerExceptHostHeader" configuration was needed, else as @vptill says, google.com will throw the CORS error
A solution for this would be allowing loadScriptsOnMainThread
to accept a function or regular expressions. This way, checking for dynamic URLs would be possible. I created a function that patches the files copied by copyLibFiles
to make loadScriptsOnMainThread
accept functions: https://gist.github.com/stldo/74a382ec9044315197cbcd0cf4832b1d. It seems to be working fine on Gatsby — at least no errors are displayed in the console —, but I don't have access to Google Analytics right now to confirm if the events are really coming.
Just call it after copyLibFiles
:
import { copyLibFiles } from "@builder.io/partytown/utils";
import { enableLoadScriptsOnMainThreadCallback } from "./partytown";
export async function onPreBootstrap() {
const basePath = path.resolve("static", "~partytown");
await copyLibFiles(basePath);
await enableLoadScriptsOnMainThreadCallback(basePath);
}
Then, in the Partytown
component:
<Partytown
key="partytown"
forward={["dataLayer.push", "gtag"]}
loadScriptsOnMainThread={(url: string) => {
return url.startsWith(
"https://googleads.g.doubleclick.net/pagead/viewthroughconversion",
);
}}
/>
Is there any chance we could get a PR with at least a breaking test? Once we have a breaking test this would be so much easier to fix for us...
@mhevery I added the setup to tests/react-app, but cant create a PR since I don't have read access. I'm not very familiar on how to do this with a repo I dont own
its very simple setup, basically you have
GoogleAnalytics.tsx
import React from 'react';
const trackingScript = (GA_TRACKING_ID: string, SITE_NAME: string, GA_TAG_ID: string) => {
return `
window.GoogleAnalyticsObject = 'ga';
window.ga = function () {
window.ga.q = window.ga.q || [];
window.ga.q.push(arguments);
};
window.ga.l = 1 * new Date();
window.ga("create", "${GA_TRACKING_ID}", "${SITE_NAME}.com");
${GA_TAG_ID ? `window.ga("require", "${GA_TAG_ID}")` : ""};
window.ga("send", "pageview");
window.ga("require", "ecommerce");`;
};
interface GoogleAnalyticsProps {
GA_TRACKING_ID: string;
SITE_NAME: string;
GA_TAG_ID: string;
}
export const GoogleAnalytics: React.FC<GoogleAnalyticsProps> = (props) => {
return (
<>
<script
type="text/partytown"
async
defer
src="https://www.google-analytics.com/analytics.js"
/>
<script
type='text/partytown'
defer
dangerouslySetInnerHTML={{
__html: trackingScript(props.GA_TRACKING_ID, props.SITE_NAME, props.GA_TAG_ID),
}}
/>
</>
);
};
GoogleTagManager.tsx
import React from 'react';
const trackingScript = (GA_TAG_ID: string) => {
return `
window.dataLayer = window.dataLayer || [];
window.gtag = function () {
dataLayer.push(arguments);
};
window.gtag('js', new Date());
window.gtag('config', '${GA_TAG_ID}');
`;
};
interface GoogleTagManagerProps {
GA_TAG_ID: string;
}
export const GoogleTagManager: React.FC<GoogleTagManagerProps> = (props) => {
return (
<>
<script
type="text/partytown"
async
src={`https://www.googletagmanager.com/gtag/js?id=${props.GA_TAG_ID}`}
/>
<script
type='text/partytown'
defer
dangerouslySetInnerHTML={{
__html: trackingScript(props.GA_TAG_ID),
}}
/>
</>
);
};
Add the 2 files to the head of the page with the correct ids GA_TAG_ID - google tag id GA_TRACKING_ID - google analytics id SITE_NAME - some site name
when you load this and look at the network calls you'll see that google tag manger calls "googleads.g.doubleclick.net" which then redirects to https://www.google.com/pagead/1p-conversion/XYZ/ and https://www.google.com/pagead/1p-user-list/XYZ/
Partytown's "resolveUrl" only captures up to googleads.g.doubleclick.net, so its not so trivial to create a reverse proxy for it because when it redirects to google.com you'll get a CORS issue. As I mentioned in my solution, all you need to do is add a request policy to the reverse proxy (that points to googleads.g.doubleclick.net) to forward all the parameters in viewer request, this allows CORS to work thru the whole chain of redirects
@mhevery I added the setup to tests/react-app, but cant create a PR since I don't have read access. I'm not very familiar on how to do this with a repo I dont own
- Fork the repo https://github.com/BuilderIO/qwik/fork
- Create a tests here: https://github.com/BuilderIO/partytown/tree/main/tests/integrations
- Send a PR please..
I initialized google ads using partytown, and it is great! Did that with reverse proxying on every url (just wait for the cors error and do reverse proxy for that url, then catch another in console log and so on... there is about 10 urls). Also pay attention on some responses if your reverse proxy server is not in same country/state as your website is. I had a problem with one url which response was different when i requested it from reverse proxy server comparing to response requested from my website. I just created another one in my country and it worked!
The problem I saw is that it loads only few of possible ads. As i am using setupAd in combination with google ads I suspect that it only loads ads from setupAd or Google (not sure). I can only say I am getting this warning:
Partytown unable to get cross-origin cookie: // gum.criteo url loads iframe
and there in that iframe i can see: document.cookie
And it is expected behavior as partytown says:
If an iframe was created with Partytown, and the iframe is cross-origin, then Partytown is unable to get or set the origin’s document.cookie, localStorage or sessionStorage. The code will continue to work, however, cookies and storage will not persist.
Setting document.cookie is a noop and will not assign anything, and getting a cookie will always return an empty string. Both localStorage and sessionStorage work as expected, however, the localStorage data is not saved across browser sessions. In these scenarios a console.warn will be logged.
Then i tried to load that iframe on main thread using loadScriptsOnMainThread
but it doesn't work (because it works only with js url and id or it doesn't work for dynamic loaded scripts).
And that's where i got stuck. Does anyone have an idea how to "bypass" that behavior? Is there any chance i can forward only event calling that url?