Your Tag Is Installed Correctly — So Why Is the Conversion Still "Inactive"?
You've set up your Google Ads conversion tracking. The Google tag is loading. Tag Assistant says "Tag found." You've got the conversion event code firing on form submission. Everything looks right. But when you check your Google Ads dashboard, the conversion action still shows "Inactive" — and the Webpages verification tab is permanently empty.
If you're running a React single-page application (SPA), especially one deployed on Replit or a similar platform, this is an incredibly common problem. And the fix is something most developers completely overlook: your Google Consent Mode v2 defaults.
The Real Problem: Consent Mode Defaults
Here's what's happening. When you implement Google's Consent Mode v2 (which Google now requires for all sites using Google tags), you set default consent states for things like analytics_storage, ad_storage, ad_user_data, and ad_personalization. Many developers — and many cookie consent banner implementations — default all of these to "denied."
That seems like the safe, privacy-first choice. And for analytics_storage, it usually is. But here's the part that trips everyone up: when ad_storage defaults to "denied," Google Ads cannot verify your tag. The Google Ads verification bot visits your site, sees that ad_storage is denied by default, and essentially concludes that the tag isn't active. It doesn't wait for a user to accept cookies. It doesn't simulate consent. It just sees "denied" and moves on.
The result? Your conversion action stays "Inactive" forever, and the Webpages verification tab remains permanently empty — no matter how perfectly your tag code is actually implemented.
How to Check If This Is Your Problem
Open your website in Chrome and go to the Network tab in DevTools (press F12, then click the Network tab). Filter the requests by typing "google" in the filter box. Look for requests to googleads.g.doubleclick.net that contain "pagead/viewthroughconversion" in the URL. If you don't see these firing on initial page load, your consent defaults are blocking the Google Ads tag.
You can also check your consent state directly. Open the browser console and type dataLayer and press Enter to inspect the consent events. Look for the "consent" event with "default" as the second parameter. If ad_storage is set to "denied" in the default consent configuration, that's your culprit.
The Fix: Grant Ad Storage by Default
For US-based businesses (where GDPR doesn't apply and there's no legal requirement to default ad consent to denied), the fix is straightforward. Change your consent defaults so that ad_storage, ad_user_data, and ad_personalization all default to "granted" while keeping analytics_storage as "denied" until the user explicitly opts in.
Here's what the corrected consent default looks like:
gtag('consent', 'default', { analytics_storage: 'denied', ad_storage: 'granted', ad_user_data: 'granted', ad_personalization: 'granted' });
This tells Google: "Ads tracking is allowed by default, but analytics tracking requires explicit user consent." For US-based sites, this is perfectly compliant and is actually what Google recommends for non-GDPR regions.
The React SPA Wrinkle: Server-Side Injection
If you're running a React SPA, there's an extra complication. In a typical React app, the HTML file that gets served to browsers is your index.html — and that's where most people put their gtag code. But on platforms like Replit, the production build process can change how your HTML is served.
On Replit specifically, the Express server serves the built HTML, and if you're doing server-side injection of meta tags or structured data (which you should be for SEO), you might also be injecting your gtag scripts server-side. This means you potentially have two places where consent defaults are set: your client-side index.html and your server-side injection function.
If you only fix one and not the other, the problem persists. In development mode, Vite serves your index.html directly. In production, the server-side injection overwrites it. You need to update both to make sure the fix works everywhere.
What About the Conversion Event Itself?
Once your consent defaults are fixed and the tag is verified, you still need to make sure the actual conversion event fires correctly. For a contact form on a React SPA, this means firing the gtag conversion event in your form's success handler — after the server confirms the submission, not on button click.
The conversion event should look like this: gtag('event', 'conversion', { send_to: 'AW-XXXXXXXXXX/YYYYYYY', value: 500.0, currency: 'USD' }). Replace the send_to value with your actual conversion ID and label from Google Ads.
One important detail: don't gate your conversion event behind analytics consent. Conversion tracking is tied to your ad spend — it measures whether your ads are working. It should fire whenever ad_storage is granted, regardless of whether the user has opted into analytics tracking. These are separate consent categories for a reason.
The Deferred Loading Trap
Another common issue with React SPAs: many performance-optimized sites defer the loading of the gtag script to improve PageSpeed scores. This is smart — the Google tag manager script is render-blocking if loaded synchronously. But if you defer it too aggressively, you can create a window where the conversion event fires before gtag is even loaded.
The safest approach is to initialize the dataLayer and the gtag function stub immediately, then load the actual script asynchronously. This way, any conversion events that fire before the script loads get queued in the dataLayer and processed once the script is ready. Here's the pattern:
First, set up the dataLayer and gtag function stub synchronously: window.dataLayer = window.dataLayer || []; window.gtag = function() { dataLayer.push(arguments); }. Then set your consent defaults and configs. Finally, create a script element with async=true pointing to googletagmanager.com/gtag/js and append it to the document head. This ensures nothing is lost even if the script takes a few seconds to load.
Testing Your Fix
After making these changes, here's how to verify everything is working. First, deploy your updated code to production. Then install the Google Tag Assistant Chrome extension and visit your site. Tag Assistant should show your Google Ads tag as "Tag found" with no warnings about consent. Next, check the Network tab in DevTools — you should now see requests to googleads.g.doubleclick.net firing on page load.
For the conversion event specifically, submit a test form and watch the Network tab. You should see an additional request to googleads.g.doubleclick.net with your conversion label in the URL. If you see it, the conversion is firing correctly.
Finally, wait 24–72 hours and check your Google Ads dashboard. The conversion action status should change from "Inactive" to "Recording" or "Active." Google's verification isn't instant — it needs to detect the tag on a few visits before updating the status.
Quick Checklist
To summarize, here's everything you need to check if your Google Ads conversions are showing "Inactive" on a React SPA: Consent defaults — ad_storage, ad_user_data, and ad_personalization should default to "granted" for US-based sites. Both client and server — if you're injecting gtag server-side, update the consent defaults in both places. Conversion event timing — fire on successful form submission, not on click. Consent gating — don't gate conversion events behind analytics consent. Script loading — use a function stub to queue events before the async script loads. Deployment — make sure your production build includes all changes, not just your dev environment.
The Bottom Line
The "Inactive" conversion status is one of the most frustrating issues in Google Ads because everything looks correct from a code perspective. The tag is there, the event fires, Tag Assistant gives you a green light. But the consent defaults are silently blocking verification behind the scenes.
For US-based businesses running React SPAs, the fix is almost always the same: default ad_storage to "granted," make sure both your client and server code agree, and verify that your conversion event fires after successful form submission. Once you do that, Google Ads can finally see your tag, and your conversion tracking will come to life.
Need Help With Your Google Ads Setup?
If you're running into conversion tracking issues or want to make sure your website is properly set up for Google Ads, I can help. I build websites with proper analytics and conversion tracking from day one — no afterthought fixes needed. Schedule a free consultation and let's make sure your ad spend is actually being measured.
Building a website for a small business? I also build professional WordPress and Webflow sites for small businesses, starting at $1,000. If you or someone you know needs a site, check out my services or get in touch.
Paul Mulligan
Freelance Web Developer
Paul Mulligan is a freelance web developer based in Baltimore, MD with 10+ years of experience building WordPress and Webflow sites for small businesses. He focuses on clean design, fast performance, and real results.
Support My Open Source Work
I build free, open-source developer tools like Flavian and Aurelius. If you find my work helpful, consider supporting me on Patreon.
Support on PatreonRelated Articles
How to Fix Meta Tags on Your Replit React Site
Read ArticleSEO Strategies That Actually Work for Small Businesses
Read ArticleWhat Google's AI Overviews Mean for Your Small Business Website
Read ArticleReady to Transform Your Business's Website?
Let's discuss how I can create a website that attracts and converts more customers.
Get a Free Consultation