ignitionstack.pro v1.0 is out! Read the announcement →
Skip to Content

💡 Practical Implementation Examples - GA4 Tracking

Practical guide with real examples that show how to implement tracking across different scenarios.

📋 Table of Contents

  1. Button Tracking
  2. Form Tracking
  3. Card/List Tracking
  4. Filter Tracking
  5. Modal Tracking
  6. Scroll Tracking
  7. E-commerce Tracking

🔘 Button Tracking

Example 1: Simple Button (CTA)

"use client"; import { landingPageEvents } from "@/app/lib/analytics-events"; import { getCurrentPageMetadata } from "@/app/lib/analytics"; export default function CTAButton() { const handleClick = () => { const metadata = getCurrentPageMetadata(); landingPageEvents.storeCTAClick({ ...metadata, destination: "/pt/loja", }); }; return ( <button onClick={handleClick} className="bg-accent-orange px-6 py-3 rounded-full" > View Store </button> ); }
"use client"; import { landingPageEvents } from "@/app/lib/analytics-events"; import { getCurrentPageMetadata } from "@/app/lib/analytics"; export default function SocialLink({ platform, url }: { platform: string; url: string }) { const handleClick = () => { const metadata = getCurrentPageMetadata(); landingPageEvents.footerSocialClick({ ...metadata, social_platform: platform, destination: url, }); }; return ( <a href={url} target="_blank" rel="noopener noreferrer" onClick={handleClick} > {platform} </a> ); }

📝 Form Tracking

Example 1: Form Start

"use client"; import { useState } from "react"; import { contactEvents } from "@/app/lib/analytics-events"; import { getCurrentPageMetadata } from "@/app/lib/analytics"; export default function ContactForm() { const [hasStarted, setHasStarted] = useState(false); const handleFirstInteraction = (fieldName: string) => { if (hasStarted) return; setHasStarted(true); const metadata = getCurrentPageMetadata(); contactEvents.formStart({ ...metadata, form_id: "contact_main", }); }; return ( <form> <input name="name" onFocus={() => handleFirstInteraction("name")} placeholder="Your name" /> <input name="email" onFocus={() => handleFirstInteraction("email")} placeholder="Your email" /> </form> ); }

Example 2: Submit with Success/Error

"use client"; import { contactEvents } from "@/app/lib/analytics-events"; import { getCurrentPageMetadata } from "@/app/lib/analytics"; export default function ContactForm() { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); const formData = new FormData(e.currentTarget as HTMLFormElement); const subject = formData.get("subject") as string; try { const response = await sendForm(formData); if (response.success) { // Track success const metadata = getCurrentPageMetadata(); contactEvents.formSubmit({ ...metadata, form_id: "contact_main", form_subject: subject, success: true, }); } else { // Track error const metadata = getCurrentPageMetadata(); contactEvents.formError({ ...metadata, form_id: "contact_main", error_type: "validation", error_field: response.errorField, }); } } catch (error) { const metadata = getCurrentPageMetadata(); contactEvents.formError({ ...metadata, form_id: "contact_main", error_type: "network", }); } }; return ( <form onSubmit={handleSubmit}> {/* form fields */} </form> ); }

🃏 Card/List Tracking

Example 1: Blog Post Card

"use client"; import Link from "next/link"; import { blogEvents } from "@/app/lib/analytics-events"; import { getCurrentPageMetadata } from "@/app/lib/analytics"; type BlogPostCardProps = { post: { id: string; title: string; slug: string; category: string; }; }; export default function BlogPostCard({ post }: BlogPostCardProps) { const handleClick = () => { const metadata = getCurrentPageMetadata(); blogEvents.postCardClick({ ...metadata, post_id: post.id, post_title: post.title, post_category: post.category, }); }; return ( <Link href={`/blog/${post.slug}`} onClick={handleClick} > <h3>{post.title}</h3> <span>{post.category}</span> </Link> ); }

Example 2: Template Card with Multiple Actions

"use client"; import { storeEvents } from "@/app/lib/analytics-events"; import { getCurrentPageMetadata } from "@/app/lib/analytics"; type TemplateCardProps = { template: { id: string; name: string; price: number; priceType: "free" | "one-time" | "subscription"; demoUrl: string; }; }; export default function TemplateCard({ template }: TemplateCardProps) { const metadata = getCurrentPageMetadata(); const handleDemoClick = () => { storeEvents.templateDemoClick({ ...metadata, template_id: template.id, template_name: template.name, }); }; const handlePurchaseClick = () => { storeEvents.initiateCheckout({ ...metadata, template_id: template.id, template_name: template.name, price: template.price, price_type: template.priceType, }); }; return ( <div> <h3>{template.name}</h3> <p>R$ {template.price}</p> <a href={template.demoUrl} target="_blank" onClick={handleDemoClick} > View Demo </a> <button onClick={handlePurchaseClick}> {template.priceType === "free" ? "Download" : "Comprar"} </button> </div> ); }

🔍 Filter Tracking

Example 1: Filter Dropdown

"use client"; import { blogEvents } from "@/app/lib/analytics-events"; import { getCurrentPageMetadata } from "@/app/lib/analytics"; export default function CategoryFilter() { const handleFilterChange = (category: string) => { const metadata = getCurrentPageMetadata(); blogEvents.filterChange({ ...metadata, filter_type: "category", filter_value: category, }); }; return ( <select onChange={(e) => handleFilterChange(e.target.value)}> <option value="all">Todas</option> <option value="frontend">Frontend</option> <option value="backend">Backend</option> <option value="mobile">Mobile</option> </select> ); }
"use client"; import { useState } from "react"; import { blogEvents } from "@/app/lib/analytics-events"; import { getCurrentPageMetadata } from "@/app/lib/analytics"; export default function SearchBar({ onSearch }: { onSearch: (query: string) => void }) { const [query, setQuery] = useState(""); const handleSearch = () => { const results = performSearch(query); // your search logic const metadata = getCurrentPageMetadata(); blogEvents.searchQuery({ ...metadata, search_query: query, results_count: results.length, }); onSearch(query); }; return ( <div> <input value={query} onChange={(e) => setQuery(e.target.value)} onKeyPress={(e) => e.key === "Enter" && handleSearch()} placeholder="Buscar..." /> <button onClick={handleSearch}>Buscar</button> </div> ); }

🪟 Modal Tracking

Example 1: Modal View (Impression)

"use client"; import { useEffect } from "react"; import { insightsEvents } from "@/app/lib/analytics-events"; import { getCurrentPageMetadata } from "@/app/lib/analytics"; type LeadMagnetModalProps = { isOpen: boolean; insightId: string; leadMagnetTitle: string; }; export default function LeadMagnetModal({ isOpen, insightId, leadMagnetTitle }: LeadMagnetModalProps) { useEffect(() => { if (isOpen) { const metadata = getCurrentPageMetadata(); insightsEvents.leadMagnetView({ ...metadata, insight_id: insightId, lead_magnet_title: leadMagnetTitle, }); } }, [isOpen, insightId, leadMagnetTitle]); if (!isOpen) return null; return ( <div className="modal"> <h2>{leadMagnetTitle}</h2> {/* modal content */} </div> ); }

Example 2: Modal Action (Download)

"use client"; import { insightsEvents } from "@/app/lib/analytics-events"; import { getCurrentPageMetadata } from "@/app/lib/analytics"; type LeadMagnetModalProps = { insightId: string; leadMagnetTitle: string; }; export default function LeadMagnetModal({ insightId, leadMagnetTitle }: LeadMagnetModalProps) { const [email, setEmail] = useState(""); const handleDownload = async () => { const hasEmail = email.trim().length > 0; // Track download const metadata = getCurrentPageMetadata(); insightsEvents.leadMagnetDownload({ ...metadata, insight_id: insightId, lead_magnet_title: leadMagnetTitle, email_captured: hasEmail, }); // Proceed with download if (hasEmail) { await saveEmail(email); } downloadFile(); }; return ( <div> <input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email (optional)" /> <button onClick={handleDownload}> Download </button> </div> ); }

📜 Scroll Tracking

Example 1: Reading Progress (Blog Post)

"use client"; import { useEffect, useState } from "react"; import { blogEvents } from "@/app/lib/analytics-events"; import { getCurrentPageMetadata } from "@/app/lib/analytics"; type ReadingProgressProps = { postId: string; }; export default function ReadingProgress({ postId }: ReadingProgressProps) { const [trackedMilestones, setTrackedMilestones] = useState<number[]>([]); useEffect(() => { const handleScroll = () => { const windowHeight = window.innerHeight; const documentHeight = document.documentElement.scrollHeight - windowHeight; const scrolled = window.scrollY; const progress = Math.round((scrolled / documentHeight) * 100); // Track milestones: 25%, 50%, 75%, 100% const milestones = [25, 50, 75, 100]; milestones.forEach((milestone) => { if ( progress >= milestone && !trackedMilestones.includes(milestone) ) { const metadata = getCurrentPageMetadata(); blogEvents.readingProgress({ ...metadata, post_id: postId, progress_percentage: milestone, }); setTrackedMilestones(prev => [...prev, milestone]); } }); }; window.addEventListener("scroll", handleScroll); return () => window.removeEventListener("scroll", handleScroll); }, [postId, trackedMilestones]); return null; // This component does not render anything }

Example 2: Scroll to Section

"use client"; import { uiEvents } from "@/app/lib/analytics-events"; import { getCurrentPageMetadata } from "@/app/lib/analytics"; export default function ScrollToSectionButton({ sectionId }: { sectionId: string }) { const handleClick = () => { const metadata = getCurrentPageMetadata(); uiEvents.scrollToSection({ ...metadata, section_id: sectionId, }); // Scroll to section const element = document.getElementById(sectionId); element?.scrollIntoView({ behavior: "smooth" }); }; return ( <button onClick={handleClick}> Go to {sectionId} </button> ); }

🛒 E-commerce Tracking

Example 1: Initiate Checkout (Stripe)

"use client"; import { storeEvents } from "@/app/lib/analytics-events"; import { getCurrentPageMetadata } from "@/app/lib/analytics"; import { useStripeCheckout } from "@/app/hooks/use-stripe"; type BuyButtonProps = { template: { id: string; name: string; price: number; priceType: "one-time" | "subscription"; stripePriceId: string; }; }; export default function BuyButton({ template }: BuyButtonProps) { const { initiateCheckout, isLoading } = useStripeCheckout(); const handleBuy = async () => { // Track initiate checkout const metadata = getCurrentPageMetadata(); storeEvents.initiateCheckout({ ...metadata, template_id: template.id, template_name: template.name, price: template.price, price_type: template.priceType, }); // Proceed with Stripe checkout await initiateCheckout({ priceId: template.stripePriceId, isSubscription: template.priceType === "subscription", metadata: { templateId: template.id, templateName: template.name, }, }); }; return ( <button onClick={handleBuy} disabled={isLoading}> {isLoading ? "Processing..." : "Buy"} </button> ); }

Example 2: Purchase (Success Page)

"use client"; import { useEffect } from "react"; import { useSearchParams } from "next/navigation"; import { storeEvents } from "@/app/lib/analytics-events"; import { getCurrentPageMetadata } from "@/app/lib/analytics"; export default function PurchaseSuccessPage() { const searchParams = useSearchParams(); useEffect(() => { const templateId = searchParams.get("template"); const sessionId = searchParams.get("session_id"); if (templateId && sessionId) { // Fetch purchase details from your API fetchPurchaseDetails(sessionId).then((purchase) => { const metadata = getCurrentPageMetadata(); storeEvents.purchase({ ...metadata, template_id: purchase.templateId, template_name: purchase.templateName, price: purchase.amount, price_type: purchase.priceType, transaction_id: purchase.transactionId, }); }); } }, [searchParams]); return ( <div> <h1>Purchase completed successfully!</h1> </div> ); }

1. Always call getCurrentPageMetadata()

// ✅ GOOD const metadata = getCurrentPageMetadata(); landingPageEvents.heroCTAClick({ ...metadata, destination: "/pt#projects", cta_type: "primary", }); // ❌ BAD - missing parameters landingPageEvents.heroCTAClick({ destination: "/pt#projects", cta_type: "primary", });

2. Track at the right time

// ✅ GOOD - Track on click <button onClick={() => { trackEvent(); performAction(); }}> Click me </button> // ❌ RUIM - Track no render useEffect(() => { trackEvent(); // Will fire multiple times }, []);

3. Use type-safe event calls

// ✅ GOOD - TypeScript will validate blogEvents.postCardClick({ ...metadata, post_id: "123", post_title: "Title", post_category: "frontend", }); // ❌ BAD - incorrect parameter blogEvents.postCardClick({ ...metadata, postId: "123", // Deveria ser post_id });

4. Handle errors gracefully

try { const metadata = getCurrentPageMetadata(); blogEvents.postCardClick({ ...metadata, post_id: post.id, post_title: post.title, post_category: post.category, }); } catch (error) { // Do not break the app if tracking fails console.error("Tracking error:", error); }

🚀 Next Steps

Now that you’ve seen the examples, add tracking to:

  1. Blog List (blog-list-client.tsx)
  2. Blog Post (post-reactions.tsx, post-share.tsx)
  3. Store (templates-grid.tsx)
  4. Projects (project-card.tsx)
  5. Insights (insight-card.tsx)
  6. Footer (footer-client.tsx)

Use estes exemplos como base e adapte para seus componentes!


Related documentation: