Google Tag Manager without hydration errors #8741
Replies: 4 comments 6 replies
-
Hmm, tried this in shopify Hydrogen without success <Suspense fallback={null}>
<Script
dangerouslySetInnerHTML={{
__html: `
(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;var n=d.querySelector('[nonce]');
n&&j.setAttribute('nonce',n.nonce||n.getAttribute('nonce'));f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer',"${id}"); |
Beta Was this translation helpful? Give feedback.
-
I've turned to ChatGPT for advice. I'm very new to Remix, and I use it inside Shopify's Hydrogen. Given that Remix has to hydrate the client-side website, and that Google Tag Manager has to only run on the client side, we need to modify our Here's the code that works well for me:
After that, you'd need to modify your ContentSecurityPolicy if you still serve GTM script from googletagmanager.com For example, in
|
Beta Was this translation helpful? Give feedback.
-
I'm new to Remix, but to get around this I just inject the scripts that change the document. Would like a better way, but if your script doesn't need to block loading then this is sufficient. useEffect(() => {
const script = document.createElement('script');
script.innerHTML = `...some script that will change the DOM`;
document.body.appendChild(script);
}, []) |
Beta Was this translation helpful? Give feedback.
-
@dimileeh's solution works nice, but -
Overall, the whole solution could be placed inside export const loader = async ({ context }: LoaderFunctionArgs) => {
return json({
GTM_ID: context.cloudflare.env.GTM_ID,
});
};
export function Layout({ children }: { children: React.ReactNode }) {
const { GTM_ID } = useLoaderData<typeof loader>();
useEffect(() => {
addGtmScript(GTM_ID);
}, [GTM_ID]);
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta />
<Links />
</head>
<body>
<noscript>
<iframe
src={"https://www.googletagmanager.com/ns.html?id=" + GTM_ID}
height="0"
width="0"
style={{ display: "none", visibility: "hidden" }}
></iframe>
</noscript>
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}
let gtmScriptAdded = false;
declare global {
interface Window {
[key: string]: object[];
}
}
function addGtmScript(GTM_ID: string) {
if (!GTM_ID || gtmScriptAdded) {
return;
}
// Code copied from GTM console + added type annotations.
(function (w: Window, d: Document, s: "script", l: string, i: string) {
w[l] = w[l] || [];
w[l].push({
"gtm.start": new Date().getTime(),
event: "gtm.js",
});
const f = d.getElementsByTagName(s)[0];
const j = d.createElement<"script">(s);
const 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_ID);
gtmScriptAdded = true;
} |
Beta Was this translation helpful? Give feedback.
-
when using google tag manager I get hydration error warning spam and my hot reload breaks the css for every change so i have to refresh.
after many hours of searching: tried
partytown
, triedClientOnly
, tried delaying script fire (which works but unreliable for slower connections), tried using gtag and giving up on gtm (it works but its not worth it because gtm has many integrations), tried usingExternalScript
from remix-utils, tried just living with the errors on production but not firing on dev, triedsuppressHydrationError
keyword in script tags.in the end the cute trick that worked was
Suspense
:here is a snippet of my
root.tsx
:works like a charm :D
Beta Was this translation helpful? Give feedback.
All reactions