This is a developer guide. The steps below require access to your Hydrogen codebase and familiarity with React, TypeScript, and deploying to Shopify Oxygen. If you're not a developer, please forward this article to your development team.
This guide explains how to add the TrackBee link decoration script to a Shopify Hydrogen storefront. The script makes sure tracking parameters (UTMs, gclid, fbclid, ttclid, etc.) are preserved as visitors navigate your site. Without it, this data is lost and can't be forwarded to your connected ad platforms, which means orders may not be attributed to the right ads or campaigns.
You'll touch two files: app/root.tsx and app/entry.server.tsx.
What this script does
When a visitor lands on your store from an ad, the URL contains tracking parameters. The link decoration script stores those parameters and appends them to every link on the page. It also watches for dynamically added links (from React hydration or client-side navigation) and decorates those too. This allows TrackBee to collect the data and forward it to your connected platforms (Meta, Google, TikTok, Pinterest, etc.), so those platforms can attribute the sale to the right ad.
Parameters persist across the entire browsing session. Even when a visitor navigates to a page without query parameters in the URL, links on that page still carry the original tracking data. If no parameters were ever stored, the script does nothing (zero overhead).
Prerequisites
A Shopify Hydrogen storefront deployed on Oxygen
Access to the Hydrogen repository (specifically
app/root.tsxandapp/entry.server.tsx)TrackBee account with your store connected
Choose your setup path
There are two ways to add the script. Path A is recommended for most Hydrogen projects. Use Path B only if your project doesn't have the Hydrogen Script component (e.g. an older version or a custom setup).
Path A: Using the Hydrogen Script component (recommended)
This approach uses Hydrogen's built-in Script component, which handles CSP nonces automatically and prevents React hydration conflicts.
Step 1: Import the Script component in app/root.tsx
Open your Hydrogen repository and navigate to app/root.tsx. At the top of the file, make sure Script is imported from @shopify/hydrogen:
import { useNonce, Script } from '@shopify/hydrogen';Step 2: Add the Script tag inside the body
Place the tag just before <ScrollRestoration>. Use waitForHydration to prevent React hydration conflicts since the script modifies the DOM.
// app/root.tsxexport function Layout({children}: {children?: React.ReactNode}) { const nonce = useNonce(); return ( <html lang="en"> <head> <meta charSet="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <Meta /> <Links /> </head> <body> <Outlet /> {/* TrackBee link decoration */} <Script src="https://cdn.trackbee.ai/link_decoration.js" waitForHydration /> <ScrollRestoration nonce={nonce} /> <Scripts nonce={nonce} /> </body> </html> );}Warning: Do not pass a nonce prop when using waitForHydration. Hydrogen's CSP handles external scripts via domain whitelisting instead (see Step 3).
Step 3: Whitelist the CDN domain in the Content Security Policy
Open app/entry.server.tsx and find the createContentSecurityPolicy call. Add the CDN hostname to scriptSrc. Without this, the browser will silently block the script.
// app/entry.server.tsxconst { nonce, header, NonceProvider } = createContentSecurityPolicy({ scriptSrc: [ "'self'", "https://cdn.trackbee.ai", ],});Step 4: Commit and deploy
Stage both changed files, commit, and push. Shopify Oxygen will automatically trigger a redeployment.
git add app/root.tsx app/entry.server.tsxgit commit -m "Add TrackBee link decoration script"git push
Monitor the deployment in your Shopify Admin under Online Store > Hydrogen. The script will be live once the deployment turns green, typically within 1-2 minutes.
Path B: Direct script tag injection
If your project does not use the Hydrogen Script component (e.g. an older Hydrogen version or a custom setup), you can add a plain <script> tag directly.
Step 1: Add the script tag in app/root.tsx
Open app/root.tsx and add a <script> tag inside the <head> section:
// app/root.tsxexport function Layout({children}: {children?: React.ReactNode}) { const nonce = useNonce(); return ( <html lang="en"> <head> <meta charSet="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <Meta /> <Links /> {/* TrackBee link decoration */} <script src="https://cdn.trackbee.ai/link_decoration.js" defer /> </head> <body> {children} <ScrollRestoration nonce={nonce} /> <Scripts nonce={nonce} /> </body> </html> );}Step 2: Whitelist the CDN domain in the Content Security Policy
Same as Path A, Step 3. Open app/entry.server.tsx and add the CDN hostname to scriptSrc:
// app/entry.server.tsxconst { nonce, header, NonceProvider } = createContentSecurityPolicy({ scriptSrc: [ "'self'", "https://cdn.trackbee.ai", ],});Step 3: Commit and deploy
git add app/root.tsx app/entry.server.tsxgit commit -m "Add TrackBee link decoration script"git push
Verify it's working
After deployment (or locally with npm run dev):
Open your storefront with test parameters, e.g.:
https://yourstore.com/?utm_source=google&utm_medium=cpc&gclid=TEST123
Click any link on the page. The destination URL should include the same parameters.
Navigate to a page without query parameters in the URL. Links should still be decorated (parameters are persisted in localStorage).
You can also open DevTools > Network tab and filter by
cdn.trackbee.aito confirm the script loaded with a 200 status.
Troubleshooting
Script not loading
Check the browser console for CSP errors. Make sure https://cdn.trackbee.ai is allowed in scriptSrc in app/entry.server.tsx.
Links not being decorated
Confirm parameters are stored in localStorage. Open DevTools > Application > Local Storage and look for _tb_link_decoration_params. If it's missing, visit a page with query parameters first.
Parameters missing on some links
Links added very late (e.g. inside lazy-loaded components) should still be caught by the MutationObserver. If not, make sure the script loads before those components render.
Want to reset stored parameters
Clear the _tb_link_decoration_params key from localStorage via DevTools, or run localStorage.removeItem('_tb_link_decoration_params') in the console.
Hydration mismatch errors (Path B)
Switch to Path A and use the Script component with waitForHydration to avoid React hydration conflicts.
Need help?
If you run into any issues, reach out to us via the chat widget in the bottom right corner. We're happy to help you get set up!