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:
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.
Welcome to Acme Coffee Roasters
Ethically sourced, expertly roasted. Free shipping over $40.
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.
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 tobody.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.
Brewing Guide page overrides saved
Apr 22, 2026 14:03body.page-brewing-guide-2026 .page-subtitlebody.page-brewing-guide-2026 .hero-sectionWhat 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> — Startor</body> — Endplacements. 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">"https://www.googletagmanager.com/gtag/js?id=G-AC123456"</span>><span class="kw"></template><template data-safe="script" data-attrs=""></span> window.dataLayer = window.dataLayer || []; function gtag(){ dataLayer.push(arguments); } gtag('js', new Date()); gtag('config', 'G-AC123456'); <span class="kw"></template></body></html>
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:
