DEV Community

Jakub
Jakub

Posted on

How I Added GA4 and Microsoft Clarity to 12 Lovable Apps Without a Single Build Step

If you're building MVPs with Lovable, you've probably wondered: how do I get proper analytics in there? Not just pageviews — real custom events that tell you whether your product has traction.

I run Inithouse, a portfolio of 12 live products all built with Lovable. Every single one has GA4 and Microsoft Clarity running with custom events — and none of them required a build tool, webpack config, or npm package to get analytics working.

Here's the exact pattern I use across all of them.

The Problem: Analytics in a React SPA

Lovable generates React SPAs. That means:

  • No server-side rendering to inject analytics
  • The index.html is your only guaranteed entry point
  • You need analytics loaded before React hydrates
  • You want custom events inside React components

Most tutorials tell you to npm install react-ga4 or use a provider component. That works, but in Lovable's environment, I wanted something simpler — something that works the moment the page loads, with zero dependencies.

The Solution: IIFE in index.html

The pattern is an Immediately Invoked Function Expression (IIFE) directly in your index.html. It loads both GA4 and Clarity, but only on production domains.

Here's the skeleton:

<script>
(function() {
  // Only run on production domains
  var prodDomains = [
    'yourdomain.com',
    'www.yourdomain.com'
  ];

  if (prodDomains.indexOf(window.location.hostname) === -1) {
    console.log('[Analytics] Dev mode — tracking disabled');
    window.gtag = function() {};
    return;
  }

  // --- GA4 ---
  var gaId = 'G-XXXXXXXXXX';
  var script = document.createElement('script');
  script.async = true;
  script.src = 'https://www.googletagmanager.com/gtag/js?id=' + gaId;
  document.head.appendChild(script);

  window.dataLayer = window.dataLayer || [];
  function gtag() { dataLayer.push(arguments); }

  // CRITICAL: expose gtag globally
  window.gtag = gtag;

  gtag('js', new Date());
  gtag('config', gaId);

  // --- Microsoft Clarity ---
  var clarityId = 'YOUR_CLARITY_ID';
  (function(c,l,a,r,i,t,y){
    c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
    t=l.createElement(r);t.async=1;
    t.src="https://www.clarity.ms/tag/"+i;
    y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
  })(window,document,"clarity","script",clarityId);

  console.log('[Analytics] GA4 + Clarity loaded');
})();
</script>
Enter fullscreen mode Exit fullscreen mode

Why This Works

  1. No build step — it's plain JS in the HTML file Lovable already generates
  2. Domain whitelist — no false data from localhost or .lovable.app preview URLs
  3. Stubbed gtag — your React code can call window.gtag() safely in dev mode without errors
  4. Loads before React — analytics is ready when your first component mounts

The Critical Line Everyone Misses

window.gtag = gtag;
Enter fullscreen mode Exit fullscreen mode

Without this, gtag is scoped inside the IIFE. Your React components can't reach it. Custom events silently fail. No errors in the console — just... no data in GA4.

I spent two days debugging this on my first product (Živá Fotka — an AI photo animation tool). The GA4 tag was firing, pageviews were counting, but custom events were completely invisible. This one line fixed everything.

Unified Event Tracking from React

Once window.gtag is globally available, create a single analytics.ts utility:

// src/lib/analytics.ts

export function trackEvent(
  eventName: string,
  params?: Record<string, string | number | boolean>
) {
  // GA4
  if (typeof window.gtag === 'function') {
    window.gtag('event', eventName, params);
  }

  // Clarity custom tags
  if (typeof window.clarity === 'function') {
    window.clarity('set', eventName, JSON.stringify(params || {}));
  }

  // Dev logging
  if (window.location.hostname === 'localhost') {
    console.log(`[Track] ${eventName}`, params);
  }
}
Enter fullscreen mode Exit fullscreen mode

Now every component in your app can do:

import { trackEvent } from '@/lib/analytics';

// User starts a generation
trackEvent('generation_started', { type: 'photo_animation', source: 'homepage' });

// User completes payment
trackEvent('purchase_complete', { plan: 'single', amount: 6 });

// User reaches a key page
trackEvent('page_engagement', { section: 'pricing', scroll_depth: 80 });
Enter fullscreen mode Exit fullscreen mode

One function. GA4 + Clarity + console logging. No provider wrappers, no context, no hooks.

What Custom Events to Track in an MVP

When you're pre-product-market-fit, custom events are your most important data source — not pageviews. Here's what I track across all 12 products:

Event What It Tells You
cta_click Are people interested enough to act?
generation_started Did they actually use the core feature?
generation_completed Did the feature work?
payment_initiated Willingness to pay signal
purchase_complete Revenue event
share_click Organic growth potential
error_displayed UX friction points

The pattern is: intent → action → completion → monetization → virality. Track each step and you'll know exactly where your funnel breaks.

I use this exact event structure on products ranging from Magical Song (AI song generator) to Pet Imagination (AI pet portraits) to Verdict Buddy (AI conflict resolver). Same analytics pattern, different products, consistent data.

Multi-Domain Setup

One of my products, Živá Fotka, runs the same codebase on 5 domains (CZ, SK, PL, EN, DE). The IIFE handles this naturally:

var prodDomains = [
  'zivafotka.cz',
  'zivafotka.sk',
  'zywafotka.pl',
  'alivephoto.online',
  'lebendigfoto.de'
];
Enter fullscreen mode Exit fullscreen mode

All 5 domains report into a single GA4 property. I use the hostname dimension in GA4 reports to filter by market. No need for separate properties or measurement IDs.

Clarity: The Underrated Companion

Most people stop at GA4. Don't. Microsoft Clarity gives you:

  • Session recordings — watch real users interact with your MVP
  • Heatmaps — see where people actually click vs. where you think they click
  • Rage clicks — instant signal for broken UX
  • Dead clicks — elements that look clickable but aren't

It's free, unlimited, and with the IIFE pattern, it's literally 5 lines of code. I check Clarity recordings weekly on every active product — it's the fastest way to spot UX issues that metrics alone won't show.

We track Clarity across all products: Be Recommended (AI visibility checker), Here We Ask (conversation card game), Party Challenges (party game), Watching Agents (prediction platform), and even our newest launch Audit Vibe Coding (vibecoded project audits).

Google Ads Conversions? Don't Touch the Code

If you're running Google Ads, you do not need to add conversion tracking code to your Lovable app. Instead:

  1. Set up your purchase/signup events as GA4 custom events (using the pattern above)
  2. In Google Ads, go to Tools → Conversions → Import from GA4
  3. Select the events you want as conversion actions

The Ads-GA4 integration handles everything. No extra scripts, no gtag conversion calls, no build-time configuration.

The Full Checklist

When I launch a new product (and I launch a lot — we're running 12 active products at Inithouse), here's my analytics setup checklist:

  • [ ] GA4 property created
  • [ ] Clarity project created
  • [ ] IIFE in index.html with correct IDs
  • [ ] prodDomains whitelist set
  • [ ] window.gtag = gtag; present (don't forget!)
  • [ ] analytics.ts with trackEvent() created
  • [ ] Core funnel events wired up (CTA → feature use → completion → payment)
  • [ ] Clarity recording verified (visit site, check dashboard for session)
  • [ ] GA4 Realtime report verified

Takes about 15 minutes per product. No packages. No build config. Just works.


I'm building 12+ MVPs in public with Lovable as a one-person operation (well, me and my AI agent). If you're into lean startup experiments, AI-powered development, or just want to see what happens when you ship fast and measure everything — follow along.

Check out our portfolio at inithouse.com or the Czech vibecoding community at vibecoderi.cz.

Top comments (0)