Skip to content

Migrating from a gtag-only setup

If your site already fires Google Ads conversions through gtag('event', 'conversion', { ... }) and you’re moving to Trackbridge, the migration is two phases: coexist (Trackbridge runs alongside gtag for a measurement window), then cut over (Trackbridge replaces gtag’s conversion calls). The reason for the two phases is to verify Trackbridge captures everything gtag was capturing before you turn gtag’s calls off — there’s no rolling back a missing day of conversion data.

You don’t have to remove gtag itself. Trackbridge calls gtag — the script still loads, the GA4 events still fire through it. Only the explicit gtag('event', 'conversion', ...) calls in your application code go away.

Most existing setups look like this:

// On the checkout success page
gtag('event', 'conversion', {
send_to: 'AW-1234567890/abcDEF1234',
transaction_id: order.id,
value: order.total,
currency: 'USD',
});

Maybe with user_data for enhanced conversions:

gtag('set', 'user_data', {
email_address: order.customer.email,
// ...
});
gtag('event', 'conversion', { /* ... */ });

This gets the browser side of dual-send right (you already have a transaction_id). What it’s missing: the server-side fire that catches ad blockers, ITP, and consent denials.

Phase 1: install Trackbridge alongside gtag

Section titled “Phase 1: install Trackbridge alongside gtag”

Don’t remove the existing gtag('event', 'conversion', ...) calls yet. Add Trackbridge in parallel.

  1. Install both packages and create the trackers per Quick start and Setting up Google Ads OAuth.

  2. Map your existing gtag conversion label to a friendly name in conversionActions:

    ads: {
    conversionActions: {
    purchase: 'customers/1234567890/conversionActions/9876543210',
    },
    },

    (The 'purchase' string is your friendly name; the resource name comes from the Ads UI per Mapping conversion actions.)

  3. Add serverTracker.trackConversion to your server-side checkout-success path (Stripe webhook, order-creation API route, etc.):

    await serverTracker.trackConversion({
    label: 'purchase',
    value: order.total,
    currency: 'USD',
    transactionId: order.id,
    gclid: order.gclid,
    userData: {
    email: order.customer.email,
    // ...
    },
    });

    Use the same transactionId (your order ID) as the existing gtag call. Google will dedup against it.

  4. Leave the existing gtag call in place. Do not touch the browser side yet.

Now both fire. Google sees two events with the same transaction_id and counts them once.

Let this run for at least a week — ideally two — and compare:

  • Daily conversion volume in the Ads UI — should be unchanged. Dedup means total count is the same.
  • Server-side success rate — your application logs (or debug: true warnings) tell you how many serverTracker.trackConversion calls succeeded vs failed. Aim for >99% success on a healthy network.

If volume in the Ads UI dropped, something is wrong — most likely transactionId is mismatched, causing Google to count the conversion twice from each side and then your daily-aggregate trends shift. Verify the call shapes match before continuing.

When Phase 1 has been stable for a week, replace your existing gtag conversion call with Trackbridge.

// Remove this:
gtag('set', 'user_data', { email_address: order.customer.email, /* ... */ });
gtag('event', 'conversion', {
send_to: 'AW-1234567890/abcDEF1234',
transaction_id: order.id,
value: order.total,
currency: 'USD',
});
// Add this:
import { tracker } from '@/lib/tracker.client';
await tracker.trackConversion({
label: 'purchase',
value: order.total,
currency: 'USD',
transactionId: order.id,
userData: {
email: order.customer.email,
// ...
},
});

tracker.trackConversion makes the same gtag('event', 'conversion', ...) call internally — same send_to, same transaction_id, same user_data shape. The difference is that the SDK also picks up gclid/gbraid/wbraid from cookies automatically and normalizes/hashes userData consistently with the server side.

Now you have proper dual-send: SDK on both browser and server, calls on both sides matching, gtag-side captures hopefully all the same conversions it always did, and the server side is your floor.

  • gtag.js itself. Don’t remove the script tag. Trackbridge needs window.gtag to exist; it doesn’t load the library itself.
  • Other gtag events (gtag('event', 'add_to_cart', ...), etc.). Trackbridge has tracker.trackEvent() for these, but it’s a thin wrapper — there’s no urgency to migrate them. Migrate when you want shared debug logging and consent state across all your gtag calls; otherwise, leave them.
  • gtag('config', ...) / gtag('consent', ...). Trackbridge does not call gtag('config', ...) or gtag('consent', ...) — those stay in your existing setup (typically a Google Tag Manager container or an inline script in your layout).
  • Manual SHA-256 hashing of email/phone in your application code. Trackbridge does it. Your old hash code probably uses toLocaleLowerCase or trims differently and is silently producing different hashes than Google expects. Remove it.
  • Manual capturing of gclid from URL params into your own cookie. Trackbridge does this. Remove the bespoke version (clear out the old cookie name on a deploy if you want, but it’ll age out anyway).
  • Manual click-ID forwarding code that diverges between browser and server. Use tracker.getClickIdentifiers() once, on the client, when you POST to your backend.

What if you don’t have a transaction_id today

Section titled “What if you don’t have a transaction_id today”

Some old gtag setups omit transaction_id. If yours does, Google has been deduping your conversions on best-effort heuristics (typically the gclid + timestamp window). When you migrate, always pass transactionId from day one — don’t try to match the old behavior. The SDK will warn you loudly on every call without it, and the dual-send pattern doesn’t work without it.

This is a real upgrade: your conversion counts will likely change once you start dedup-keying properly. Treat the cutover as a baseline reset.