[ ENGINEERING_GUIDE ][ GOOGLE_ADS ][ CONVERSIONS ][ MARKETING ][ ANALYTICS ]

Google Ads — conversion tracking on a business website (complete guide 2026)

May 28, 20269 min read
Author: DevStudio.itWeb & AI Studio

Google tag, conversion actions, gtag events, and fixing "incomplete setup" — step by step for service businesses on Next.js.

READ_TIME: 9 MIN_COMPLEXITY: MED_
STAMP: VERIFIED_BY_DS_

TL;DR

You have the Google tag on your site, but Google Ads still shows "incomplete setup"? In about 90% of cases the problem is not "missing code" but a mismatch across three layers: the global AW- tag, a conversion action in the Ads panel (with the correct send_to label), and an event fired only after a successful form submit (HTTP 200). On a production Next.js 15 site, split GA4 (afterInteractive) from Ads tags (lazyOnload) — otherwise you either lose leads in analytics or hurt LCP. Below: panel setup, what to send from code, and how to test with Tag Assistant.

Who this is for

  • Service businesses running lead campaigns (contact form, chatbot)
  • Owners of Next.js / React sites where marketing already added one or more AW- containers
  • People seeing a campaign diagnostics warning despite a green tag in the page preview
  • Teams connecting GA4 with Google Ads without importing conversions from Analytics

Keywords (SEO)

google ads conversion tracking, google tag conversions, google ads conversion setup, gtag send_to AW, conversion_event_submit_lead_form, incomplete google ads setup

Why the AW- tag alone is not enough

Google Ads separates tracking foundation (global tag on every page) from conversion action (business goal definition). The tag gtag/js?id=AW-XXXXXXXX plus gtag('config', 'AW-XXXXXXXX') tells Google: "this traffic belongs to this ad account." That does not yet mean the system knows which "Submit" clicks are valuable leads.

Element What it does Enough for Smart Bidding?
AW-... tag in layout Links session to Ads account No
Conversion action in panel Defines goal and label Yes — after first hits
gtag('event', ...) after form success Sends conversion signal Yes — must match that action

Campaigns using maximize conversions or target CPA need registered conversions attached to the campaign goal. While the panel shows "No data" or "Draft", the algorithm optimizes like clicks only — hence the feeling that "ads work but leads are missing from reports."

Tag architecture on Next.js 15 (production example)

In a modern business project (e.g. a software house site) you often have multiple IDs at once — normal during Ads account migration or historical campaigns.

GA4 — early, so form events are not lost

Google Analytics 4 (G-3HT7CZTN7P in this project) loads with afterInteractive in layout.tsx. The code comment is explicit: with lazyOnload, users often submit the form before gtag is ready — then generate_lead never reaches GA4, even though the lead reached CRM.

<Script
  src={`https://www.googletagmanager.com/gtag/js?id=${GA4_MEASUREMENT_ID}`}
  strategy="afterInteractive"
/>

This is a deliberate tradeoff between performance and data quality: key business events (leads) must be reliable.

Three Ads containers load with lazyOnload:

  • AW-17557280025
  • AW-17769880693
  • AW-18151506857

Each has its own gtag/js and gtag('config', 'AW-...'). The ad script does not block first paint (LCP), but is usually available before typical form completion time — unless the user submits a lead in the first 2–3 seconds (rare on a service site).

Do not remove old AW- tags when adding a new container. Historical conversions, attribution reports, and old campaigns may depend on the previous ID. Add a new config, create a new conversion action, and attach the new campaign to it.

What to send after a successful form

Count conversions only after server confirmation (response.ok), not on the "Submit" button onClick. Otherwise Ads and GA4 overcount abandoned submits and validation errors.

In this project the same pattern is used in the contact form (page.tsx) and chatbot (Chatbot.tsx):

1. GA4 event — generate_lead

gtag('event', 'generate_lead', {
  send_to: 'G-3HT7CZTN7P', // GA4_MEASUREMENT_ID constant in code
  form_type: 'contact_form', // or 'chatbot_form'
  locale: 'en',
  project_type: '...',
});

send_to with the GA4 measurement ID forces delivery to the correct Analytics property (a separate stream ≠ automatic import to Ads).

2. Google Ads event — conversion_event_submit_lead_form

gtag('event', 'conversion_event_submit_lead_form', {
  event_callback: goThanks,      // e.g. redirect to /thanks
  event_timeout: 2000,
  form_type: 'contact_form',
  project_type: '...',
  budget: '...',
});

event_callback + event_timeout: 2000 give the tag time to send the hit before navigation — without that, the browser often aborts the conversion request. The contact form also has a fallback setTimeout(goThanks, 2200) if the callback never fires.

3. Classic conversion with send_to: 'AW-xxx/label'

If you create a "Website" action in Ads with a conversion ID, Google provides:

AW-123456789/AbCdEfGhIjKlMnOpQr

Then alternatively (or additionally) send:

gtag('event', 'conversion', {
  send_to: 'AW-123456789/AbCdEfGhIjKlMnOpQr',
  value: 1.0,
  currency: 'EUR',
});

The event name conversion_event_submit_lead_form must exactly match Ads configuration. A name mismatch = zero conversions in campaign reports even if Tag Assistant flickers.

Create a conversion action

  1. Tools & settings → Conversions → New conversion action.
  2. Source: Website (or GA4 import — separate path).
  3. Category: Contact form submission or custom event matching your code.
  4. Copy conversion ID (AW-.../label) or event instructions.
  5. Status: Active (not "Draft").

In campaign settings: Goals → Conversions — select the same action that receives hits from the site. If the campaign targets AW-17769880693 but send_to or the default container points to AW-17557280025, you see traffic in one account and optimization in another — a classic cause of "incomplete setup."

24–48 hour delay

After the first production test, conversion list status often changes from "No recent activity" only after 24–48 hours. Do not restart the campaign after one test; check Last activity the next day.

"Incomplete setup" — most common causes

  1. Global tag only — no conversion action or no post-submit event.
  2. Campaign started before first conversion — Google waits for data to train the algorithm.
  3. Campaign goal ≠ action receiving events — different AW- or label.
  4. Testing on localhost / preview — Ads expects the domain used in ads (production).
  5. GA4 sees generate_lead, Ads does notseparate systems; without send_to to AW- or conversion import, data does not flow.
  6. Adblock / Brave / Firefox ETP — works for you in test, not for all clients; test incognito without extensions on the production domain.
  7. lazyOnload + very fast submit — rare but possible; consider afterInteractive for one main campaign container or delay redirect (like event_timeout).

Testing — 5–15 minutes that save weeks

Tag Assistant (Chrome)

  1. Install Tag Assistant Companion.
  2. Open the production site (same domain as in ads).
  3. Submit a test form with a unique email (test+ads2026@yourdomain.com).
  4. In preview check: GA4generate_lead; Adsconversion or conversion_event_submit_lead_form.
  5. In Google Ads → Conversions — whether a hit appeared (sometimes delayed).

Realtime (GA4)

In Reports → Realtime look for generate_lead with form_type. If it appears here but Ads is silent — the problem is the AW- layer, not the form.

Pre-budget checklist

  • AW- tag on all language versions (/pl, /en, …)
  • One canonical conversion path (form + chatbot → same Ads event)
  • Conversion action Active and linked to campaign
  • Thank-you redirect after event_callback or with ~2 s buffer
  • No false conversions on "Submit" click alone

Performance vs. data completeness

Typical layout for a business site:

  • GA4 afterInteractive — do not lose leads in marketing and CRM attribution reports.
  • Ads lazyOnload — protect LCP and Performance score (also matters for landing page experience in Ads).

If Ads diagnostics require earlier loading of the main AW-, consider afterInteractive only for the container tied to the active campaign, keeping other AW- tags lazy — not all three need to compete for the same <head> slot.

FAQ

Must I remove the old AW- tag when adding a new one?

No, if historical conversions and reports depend on the old ID. Add new gtag('config'), create a new conversion action, and attach the new campaign to the new goal. Retire old tags only after migrating reports and ending old campaigns.

How many AW- tags on one page is too many?

2–3 is often acceptable during account migration. Each loads a separate script — use lazyOnload, one conversion send point on the form, and do not fire the same conversion three times (inflation). One hit per submit beats three "just in case."

Why does GA4 see generate_lead but Google Ads does not?

GA4 and Ads are separate products. Events with send_to: G-... go to Analytics. Ads needs send_to: AW-.../label, an event defined in the account, or GA4 conversion import (with delay and different attribution rules). A shared gtag in dataLayer is not enough without correct goal configuration.

Does conversion_event_submit_lead_form replace classic conversion?

Depends on how you created the action in the panel. If Ads gave a custom event — use that exact name. If it gave AW-xxx/yyy — use gtag('event', 'conversion', { send_to: '...' }). In practice you often use both: GA4 for analytics, Ads for campaign optimization.

Will testing on a Vercel Preview URL work?

Usually not for full Ads diagnostics if ads point to the production domain. Preview may have different cookies, robots, and traffic. Test the final domain from the campaign.

Importing conversions from GA4 — when it makes sense

Google Ads can import GA4 events as conversions. Convenient when marketing "lives" in Analytics and developers only send generate_lead with send_to: G-.... Downsides:

  • Delay and different attribution than native AW- tag
  • Harder "did the hit arrive?" diagnostics in one place
  • Risk that GA4 filters exclude part of traffic (e.g. cookie consent)

For campaigns with strict CPA and short algorithm learning windows, prefer a direct hit to Ads (conversion or panel-defined event) plus GA4 in parallel — as in this project. Treat GA4 import as reporting backup, not the only optimization signal.

If the cookie banner blocks gtag before marketing consent, Ads may show "tag installed" but modeled or zero conversions until consent. Ensure:

  • Tags load per policy (e.g. Consent Mode v2),
  • Conversion tests run after accepting marketing cookies in the banner,
  • Internal docs note: "lead in CRM" ≠ "conversion in Ads" when cookies are refused.

Multilingual (/pl, /en, /de)

Each locale must have the same tag set in shared or locale layout. The form in page.tsx and chatbot send events with a locale parameter — filter Ads/GA4 reports by it so PL campaigns are not mixed with DE traffic. If you use separate Ads accounts per country, send_to must point to the correct AW- for that campaign.

Summary

Google Ads conversion tracking on Next.js is a puzzle: global AW- tag, panel action, event after API form success, consistent send_to, and patience for 24–48 h on first data. Splitting GA4 (afterInteractive) and Ads (lazyOnload) is a mature tradeoff between Core Web Vitals and not losing leads. Before increasing budget, confirm conversions with Tag Assistant on production — not localhost. With multiple AW- containers — do not remove old ones without migrating campaign goals.

Want tracking set up for your site?

About the author

We build fast websites, web/mobile apps, AI chatbots and hosting setups — with a focus on SEO and conversion.

Recommended links

From theory to production — Branchly, our hosting stack, care plans and shipped work.

LIKE HOW WE THINK? LET'S BUILD SOMETHING TOGETHER.

[ START_PROJECT_CONFIGURATION ]