Reference → Google Analytics 4

Add custom HTML, scripts, and tracking code to your site

How to inject analytics, cookie banners, and third-party widgets in SGEN

The Custom Codes area is where you paste third-party HTML snippets — Google Analytics, cookie-consent banners, chat widgets, conversion pixels, custom font links — and choose where on the page they load. Each snippet is saved with a name, a placement (<head>, <body> Start, or </body> End), and a toggle to turn it on or off without deleting it. This walkthrough is the fast path from "marketing sent me a GA4 script" to "it's collecting data on every page."

What is this for?

Custom Codes is SGEN's global injection point for cross-cutting snippets that need to live on every page: analytics loaders, tag managers, chat widgets, cookie banners, tracking pixels, and external stylesheets or font links. You write the snippet once and it renders on every public page until you turn it off. It is not a page-content tool — page HTML belongs in SG-Builder.

Good use cases

Example 1: Google Analytics 4 measurement tag. Marketing hands you the GA4 snippet from Admin → Data Streams. You paste it into a new Custom Code with placement </body> — End and activate it. On the next public-page reload, the gtag loader fires and starts recording sessions. Here is a sanitised version you can paste as-is after swapping the measurement ID:

<!-- Google tag (gtag.js) -->
<template data-safe="script" data-attrs=" async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX""></template>
<template data-safe="script" data-attrs=""> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-XXXXXXXXXX');
</template>

Once saved, the snippet renders immediately before </body> on every public page. Open your public homepage, right-click → View page source, and you'll see exactly this block near the bottom of the HTML:

Rendered GA4 output on a public page

To confirm the tag is firing, open your site in a private/incognito window, then sign in to Google Analytics and check the Realtime report. You should see your own visit appear within a minute. If you use a consent banner, make sure you accept cookies first — a declined banner will suppress the tag on purpose.

Example 2: Cookie consent banner. Your cookie platform (Cookiebot, OneTrust, Osano) gives you a <template data-safe="script" data-attrs=""></code> tag plus an optional <code><style></code> block to tune the banner position. Paste both into a single Custom Code, placement <code><head></code> (so the banner renders on first paint, before any tracking code fires). Here is a Cookiebot-shaped example:</p> <pre class="code"><code><script id="Cookiebot" src="https://consent.cookiebot.com/uc.js" data-cbid="00000000-0000-0000-0000-000000000000" data-blockingmode="auto" type="text/javascript"></template> <template data-safe="style" data-attrs="">#CybotCookiebotDialog { font-family: inherit !important; }</template>

After saving + activating, reload the public homepage in an incognito window. You should see the consent banner overlay on first load; clicking Allow or Decline writes the cookie and the banner disappears on subsequent loads.

Public site preview
https://acmecoffee.com/

Welcome to Acme Coffee Roasters

Ethically sourced, expertly roasted. Free shipping over $40.

We use cookies to improve your experience.AllowDecline

Example 3: Custom font from Google Fonts. Need a specific font not in the Theme Editor? Paste the Google Fonts <link> plus a <template data-safe="style" data-attrs=""></code> override, placement <code><head></code>:</p> <pre class="code"><code><link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet"> <style> body { font-family: 'Inter', system-ui, sans-serif; } </template>

On the next public-page reload, typography switches to Inter site-wide. If the change doesn't appear immediately, hard-reload with Ctrl+Shift+R to bypass the browser cache.

Public site preview
https://acmecoffee.com/

Acme Coffee Roasters

Ethically sourced, expertly roasted. Free shipping over $40.

Typography is now Inter across all headings and body text — set once, renders everywhere.

Example 4: Page-scoped CSS override. You need to hide the default page subtitle on your Brewing Guide landing page, but only on that one page — not site-wide. In SG-Builder, every page gets a unique body class based on its slug (for example, the /brewing-guide-2026 page has body.page-brewing-guide-2026). You can use that body class to scope your CSS so it never leaks to other pages.

/* Page-scoped CSS override — Brewing Guide 2026 landing page only */
body.page-brewing-guide-2026 .page-subtitle { display: none;
}
body.page-brewing-guide-2026 .hero-section { background-color: #1a0a00; color: #f5e6d0;
}

Paste this into a new Custom Code, placement <head>, title "Brewing Guide page overrides", Status Active. The rules only fire when body.page-brewing-guide-2026 is present — every other page is unaffected.

Why scope to body.page-<slug>? Bare selectors like .page-subtitle { display: none; } hide that element on every page of your site. Always prefix with the body class for page-specific rules. New SGEN builds follow this convention; if you see bare selectors in old Custom Codes, replace them with scoped versions before adding new ones.
Settings saved

Brewing Guide page overrides saved

Apr 22, 2026 14:03
Custom code saved and active. The override is live on acmecoffee.com/brewing-guide-2026.
Updated: body.page-brewing-guide-2026 .page-subtitlebody.page-brewing-guide-2026 .hero-section

What NOT to use this for

  • Don't put body-only HTML in a <head>-placement code. If your snippet contains <div>, <span>, <p>, <img>, <iframe>, <form>, <button>, or other body elements, SGEN will reject the save with a red error banner. Those tags belong in <body> — Start or </body> — End placements. A <head> snippet should contain only <template data-safe="script" data-attrs=""></code>, <code><style></code>, <code><link></code>, or <code><meta></code>.</li> <li><strong>Don't rely on HTML comments to mark body-placement snippets.</strong> <code><!-- Begin GTM container --></code> will be saved to the database, but SGEN strips comments from body-placement snippets at render time — they won't appear in public page source. If you need a visible marker, use a unique JavaScript variable (e.g. <code>window.__my_tag = true;</code>) or a <code>data-*</code> attribute on a wrapper <code><div></code> instead.</li> <li><strong>Don't use Custom Codes for large page content.</strong> If you're pasting a pricing table, a hero section, or a testimonial block, use SG-Builder on the page itself. Custom Codes is for cross-cutting scripts and tiny inline overrides, not copy.</li> <li><strong>Don't use it for site-wide CSS.</strong> Use <strong>Custom → CSS</strong> instead. CSS pasted here as <code><style></code> emits inline on every page, which is harder to cache and slower than the dedicated Custom CSS area.</li> <li><strong>Don't paste secrets.</strong> API keys, webhook tokens, private service URLs — whatever you paste is rendered verbatim in the public page source. Anyone who views source can read it. Tracking IDs (like GA4 <code>G-XXXXXXXXXX</code>) are fine; private keys are not.</li> <li><strong>Don't trust the "deleted" confirmation on its own.</strong> If you're scripting a delete via the API or using the per-row Trash link, reload the list to verify the row really moved to Trash — the confirmation toast alone isn't proof.</li> </ul> <h3>Before you start</h3> <ul> <li>You are signed in to SGEN as an admin with Custom Codes access.</li> <li>You have the snippet to paste, and you know which of the three placements it needs: <code><head></code>, <code><body> — Start</code>, or <code></body> — End</code>.</li> <li>Your snippet is safe to render on every public page. Custom Codes is site-wide — there's no per-page scoping today.</li> <li>If you need per-page injection, use SG-Builder's Custom HTML component on the page itself.</li> </ul> <h3>Where to go</h3> <ol> <li>Open the left navigation.</li> <li>Select <strong>Custom → Codes</strong> to open the list of saved snippets at <code>/sg-admin/custom_codes/</code>.</li> <li>Click <strong>+ Add New</strong> (top-right) to create a new snippet, or click an existing row title to edit it.</li> </ol> <div class="sgen-demo-wrap"><div class="sgen-demo-label">Custom Codes list view</div><div class="demo-5"><div class="breadcrumb"><a href="javascript:void(0)">Dashboard</a> / All Custom Codes</div> <div class="page-head"> <div class="title-wrap"> <div class="title-ic" aria-hidden="true"></></div> <div> <h1>Custom Codes</h1> <div class="sub">Manage custom HTML snippets.</div> </div> </div> <a class="add-btn" href="javascript:void(0)" data-msg="Would navigate to /sg-admin/custom_codes/add_new?type=all"><span>+</span> Add New</a> </div> <div class="tools"> <nav class="pills" aria-label="status filter"> <a class="pill active" href="javascript:void(0)" data-msg="status=all">All Custom Codes<span class="ct">2</span></a> <a class="pill" href="javascript:void(0)" data-msg="status=active">Active<span class="ct">1</span></a> <a class="pill" href="javascript:void(0)" data-msg="status=inactive">Inactive<span class="ct">1</span></a> <a class="pill" href="javascript:void(0)" data-msg="status=trash">Trash<span class="ct">0</span></a> </nav> <form class="search" role="search"><input type="text" name="s" placeholder="Search..."><button type="submit" class="apply-btn">Search</button></form> </div> <form class="bulk-row"> <select name="bulk_action"> <option>Action For Selected</option> <option value="active">Move to Active</option> <option value="inactive">Move to Inactive</option> <option value="trash">Move to Trash</option> </select> <button type="button" class="apply-btn" data-msg="Would POST /Admin_Custom_Codes_Actions/do_table">Apply</button> </form> <table class="tbl" role="table"> <thead><tr> <th style="width:30px"><input type="checkbox" aria-label="select all"></th> <th>Title</th><th>Status</th><th>Placement</th><th>Priority</th><th>Author</th><th>Created</th> </tr></thead> <tbody> <tr> <td><input type="checkbox"></td> <td> <a class="row-title" href="javascript:void(0)" data-msg="Would open /sg-admin/custom_codes/edit/1">Google Analytics 4</a> <div class="row-actions"><a href="javascript:void(0)" data-msg="Would open /edit/1">Edit</a> | <a href="javascript:void(0)" data-msg="Would POST /code_delete">Trash</a></div> </td> <td class="status-cell active">Active</td> <td><span class="chip"></div></div> <p>The list shows four status pills at the top (All / Active / Inactive / Trash) with live counts, a search box, a bulk-actions dropdown, and a row per saved snippet. Each row has Edit and Trash hover actions. The <strong>Placement</strong> column shows which of the three positions the snippet lives at; the <strong>Priority</strong> column controls order when multiple snippets share a placement (lower = earlier).</p> <h3>Steps</h3> <h4>1. Open the Add New form</h4> <p>Click <strong>+ Add New</strong> on the list toolbar. SGEN navigates to <code>/sg-admin/custom_codes/add_new?type=all</code>.</p> <div class="sgen-demo-wrap"><div class="sgen-demo-label">Add New button</div><div class="demo-6"><a class="add-btn" href="javascript:void(0)" data-msg="Would navigate to /sg-admin/custom_codes/add_new?type=all"><span class="ic">+</span> Add New</a></div></div> <h4>2. Fill in the form</h4> <p>Give the snippet a descriptive <strong>Title</strong> (this is for your reference — it never appears on the public site). Paste the snippet body into the <strong>Code snippet</strong> editor. Choose the <strong>Placement</strong> that matches the vendor's instructions. Set <strong>Priority</strong> if order matters (lower = earlier). Toggle <strong>Status</strong> to <em>Active</em> when you're ready for it to render on the public site.</p> <div class="sgen-demo-wrap"><div class="sgen-demo-label">Add New form</div><div class="demo-7"><div class="breadcrumb">Dashboard / Codes / New</div> <div class="page-head"> <a class="back" href="javascript:void(0)" data-msg="Would navigate to /sg-admin/custom_codes" aria-label="Back">←</a> <div> <h1>Code Manage</h1> <div class="sub">Create and update custom code snippets.</div> </div> </div> <form class="grid" action="javascript:void(0)"> <div class="panel"> <label class="fld"> <strong>Title</strong> <input type="text" name="title" value="My Code"> </label> <label class="fld"> <strong>Code snippet</strong> <div class="editor-wrap"> <div class="editor-head"><span>HTML</span><span>CodeMirror</span></div> <div class="editor" role="textbox" aria-multiline="true" aria-label="Code editor"><span class="cm"></span></div> </div> </label> <div class="two-col"> <label class="fld" style="margin:0"> <strong>Placement</strong> <select name="meta[placement]"> <option value="head"><head></option> <option value="body_start"><body> — Start</option> <option value="body_end" ></div></div> <blockquote><strong>Placement quick reference.</strong> <code><head></code> is for loader scripts, fonts, and metadata. <code><body> — Start</code> is for first-paint scripts like GTM pixels. <code></body> — End</code> is for late-binding widgets and anything you don't want to block page paint.</blockquote> <div class="sgen-demo-wrap"><div class="sgen-demo-label">Placement dropdown with tag-hint labels</div><div class="demo-8"><label class="fld"> <strong>Placement</strong> <select name="meta[placement]"> <option value="head"><head></option> <option value="body_start"><body> — Start</option> <option value="body_end" selected></div></div> <h4>3. Save the snippet</h4> <p>Click <strong>Create a Code</strong>. If everything is valid, SGEN saves the row, redirects you to the edit view, and flashes a green success message. The snippet begins rendering on the public site on the very next page load — there is no deploy step.</p> <div class="sgen-demo-wrap"><div class="sgen-demo-label">Settings saved</div><div class="demo-9"><div class="flash" role="status" aria-live="polite"> <div class="ic" aria-hidden="true">✓</div> <div class="body"> <div class="headline"> <h4>Custom Code saved</h4> <span class="ts">Apr 22, 2026 14:03</span> </div> <div class="msg">A code has been successfully created! It is now active on every public page.</div> <div class="fields"><strong>Updated:</strong> <code>Google Analytics 4</code><code>placement: </body> — End</code><code>priority: 1</code><code>status: Active</code></div> </div> </div></div></div> <blockquote class="warn"><strong>If the save fails with a red error banner:</strong> you probably put body-only HTML in a <code><head></code>-placement code. SGEN rejects the save and your input is preserved in the form so you can fix it. The exact error looks like this:</blockquote> <div class="sgen-demo-wrap"><div class="sgen-demo-label">Head-placement rejection error</div><div class="demo-10"><div class="flash-err" role="alert"> <span class="ic" aria-hidden="true">!</span> <div> <div class="ttl">Save rejected</div> <div>Head placement cannot include body-only HTML elements like <code><div></code>, <code><p></code>, <code><form></code>, etc. Your snippet was not saved. Move the snippet to <strong><body> — Start</strong> or <strong></div></div> <p>Switch the Placement dropdown to <code><body> — Start</code> or <code></body> — End</code>, and click Create a Code again.</p> <h4>4. Verify on the public site</h4> <p>Open your public homepage in a new tab. Right-click → <strong>View page source</strong>. Search the source for a string unique to your snippet (e.g. the GA4 measurement ID <code>G-XXXXXXXXXX</code>). You should find it in the expected placement:</p> <ul> <li><strong><code><head></code></strong> — between <code><head></code> and <code></head></code>.</li> <li><strong><code><body> — Start</code></strong> — immediately after the opening <code><body></code> tag.</li> <li><strong><code></body> — End</code></strong> — immediately before the closing <code></body></code> tag.</li> </ul> <p>For analytics, also check the vendor's own dashboard: Google Analytics <strong>Realtime</strong>, Tag Manager <strong>Preview</strong>, or Cookiebot's <strong>Scanning history</strong>. If your visit shows up there within a minute of loading the public site, the snippet is live.</p> <div class="sgen-demo-wrap"><div class="sgen-demo-label">Public site preview</div><div class="demo-11"><div class="browser"> <div class="chrome"> <div class="dots"><span class="dot"></span><span class="dot"></span><span class="dot"></span></div> <div class="url">https://acmecoffee.com/</div> </div> <div class="frame"><div class="inject-label">View source near </body></div> <div class="code-inspect"><span class="cmt"><!-- Google tag (gtag.js) --></span> <script async src=<span class="str">&quot;https://www.googletagmanager.com/gtag/js?id=G-AC123456&quot;</span>><span class="kw"></template><template data-safe="script" data-attrs=""></span> window.dataLayer = window.dataLayer || []; function gtag(){ dataLayer.push(arguments); } gtag(&#39;js&#39;, new Date()); gtag(&#39;config&#39;, &#39;G-AC123456&#39;); <span class="kw"></template></body></html>
Visible to anyone who opens View-source on your live page. Confirms the snippet is rendering at the chosen placement.

5. Edit or disable later

Click a row's title (or its Edit hover action) to re-open the form with the stored values pre-filled. You can change the body, swap the placement, re-prioritise, or toggle Status to Inactive to stop it rendering without deleting the row:

Edit form with sidebar metadata

Code Manage

Create and update custom code snippets.

Restore brings the row back as Inactive (not re-activated automatically — you have to flip the switch explicitly). Delete Permanently opens a confirmation modal:

Delete Confirmation modal

Are you sure?

This will permanently delete the code. You won't be able to revert this!

Clicking Yes, delete it! permanently removes the row from the database. There's no undo.

The complete flow

For a new analytics setup in one session:

  1. Custom → Codes → + Add New. Title "GA4 measurement tag", paste the gtag snippet, placement </body> — End, priority 1, Status Active.
  2. Create a Code. Confirm the green success flash.
  3. Add a second snippet for Cookiebot. Title "Cookiebot consent", placement <head>, priority 1, Status Active.
  4. Open the public homepage in incognito — banner overlays on first load, gtag starts collecting after consent.
  5. View page source. The Cookiebot script is in <head>; the GA4 script is before </body>.
  6. Vendor dashboards. In Google Analytics Realtime and Cookiebot Scanning, confirm your own visit appears within a minute.

What success looks like

  • The list shows your new snippet with a green Active pill and your chosen Placement and Priority.
  • The public homepage view-source contains the exact snippet you pasted, in the exact position you specified.
  • For analytics tags: the vendor's Realtime/dashboard view shows your own visit appearing within a minute of loading the public site.
  • For consent banners: the banner appears on first visit in incognito, and is dismissed correctly.

What to do if it does not work

  • I clicked Create a Code and got a red error banner. Body-only HTML in a head-placement snippet is rejected. Switch Placement to <body> — Start or </body> — End.
  • My snippet saved but I don't see it on the public site. Check Status — Inactive rows are saved but not rendered. Flip the Status switch to Active and save again. Then hard-reload the public page (Ctrl+Shift+R).
  • The public page still shows the old version of my snippet. CSS and edge caches can delay propagation. Wait 60 seconds and hard-reload; if it still lags, check with your site admin about CDN caching.
  • My HTML comment disappeared from the public page. Comments in body-placement snippets are stripped at render time. Put the marker inside a <script> block or a data-* attribute on a wrapper <div> instead.
  • The GA collect / Cookiebot call doesn't fire. Check the vendor's measurement ID in the snippet is correct. Confirm your browser isn't running an ad-blocker that kills the vendor domain.
  • My snippet stopped working. Check the Trash tab — maybe someone moved it there. Restore it (lands as Inactive), then flip Status to Active.

Next step

  • To inject site-wide CSS, use Custom → CSS instead (purpose-built, cache-friendlier).
  • To inject HTML on a specific page only, use the Custom HTML block in SG-Builder on that page.
  • To change site typography, colours, or header/footer markup, use Appearance — faster and safer than editing chrome via Custom Codes.
On this page