diff --git a/packages/visual-editor/src/components/GTMBody.tsx b/packages/visual-editor/src/components/GTMBody.tsx new file mode 100644 index 000000000..ce50115d0 --- /dev/null +++ b/packages/visual-editor/src/components/GTMBody.tsx @@ -0,0 +1,52 @@ +import React, { useMemo } from "react"; +import { useDocument } from "@yext/visual-editor"; + +const GTM_ID_REGEX = /^GTM-[A-Z0-9]+$/; + +/** + * Adds the Google Tag Manager (noscript) iframe to the body. + * This is required for GTM to function properly when JavaScript is disabled. + */ +export const GTMBody: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => { + const streamDocument = useDocument(); + + const visualEditorConfig: Record | null = useMemo(() => { + if (!streamDocument?.__?.visualEditorConfig) { + return null; + } + + try { + return JSON.parse(streamDocument.__.visualEditorConfig); + } catch (_) { + console.warn( + "Failed to parse visualEditorConfig for GTM. Skipping adding GTM iframe." + ); + return null; + } + }, [streamDocument]); + + const googleTagManagerId = visualEditorConfig?.googleTagManagerId; + + if (!googleTagManagerId || !GTM_ID_REGEX.test(googleTagManagerId)) { + return <>{children}; + } + + return ( + <> + {/* Google Tag Manager (noscript) */} +