Complete Analytics & Ad Tracking Setup for Next.js — GA4, Google Ads & Meta Pixel
Why Tracking Matters More Than You Think
You've built a beautiful website. Traffic is coming in. You're running ads on Google and Meta. But here's the problem — you have no idea what's actually working.
Without proper tracking setup:
- You don't know which pages visitors spend time on
- You can't tell which ad campaign brought paying customers
- You're spending money on ads with zero visibility into ROI
- You're making business decisions based on guesswork
I set up analytics and ad tracking on every client website I build. It takes about 30 minutes and gives you data worth thousands of dollars in better marketing decisions.
Here's the exact setup I use in every Next.js project.
What We're Setting Up
- Google Analytics 4 (GA4) — track visitors, page views, user behavior, and custom events
- Google Ads Conversion Tracking — measure which ads lead to form submissions, purchases, or signups
- Meta Pixel (Facebook Pixel) — track visitors from Facebook/Instagram ads and build retargeting audiences
All three work together. GA4 gives you the full picture, Google Ads tells you which keywords convert, and Meta Pixel lets you retarget website visitors on Instagram and Facebook.
Prerequisites
Before you start, you'll need:
- A Next.js project using App Router (Next.js 13+)
- A Google Analytics account — get your Measurement ID (starts with
G-) - A Google Ads account — get your Conversion ID and Conversion Label
- A Meta Business Suite account — get your Pixel ID
Part 1: Google Analytics 4 (GA4) Setup
Step 1: Create Your GA4 Property
- Go to Google Analytics
- Click Admin (gear icon) → Create Property
- Enter your website name and URL
- Once created, go to Data Streams → Web → copy your Measurement ID (looks like
G-XXXXXXXXXX)
Step 2: Add the GA4 Script to Next.js
Create a reusable analytics component. This loads the Google Tag (gtag.js) script and initializes GA4:
// src/components/GoogleAnalytics.tsx
"use client";
import Script from "next/script";
const GA_MEASUREMENT_ID = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID;
export default function GoogleAnalytics() {
if (!GA_MEASUREMENT_ID) return null;
return (
<>
<Script
src={`https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`}
strategy="afterInteractive"
/>
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GA_MEASUREMENT_ID}');
`}
</Script>
</>
);
}Step 3: Add It to Your Root Layout
// src/app/layout.tsx
import GoogleAnalytics from "@/components/GoogleAnalytics";
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<GoogleAnalytics />
{children}
</body>
</html>
);
}Step 4: Set Environment Variable
Add your Measurement ID to .env.local:
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXXThat's it for basic GA4. You'll immediately start seeing page views, user count, session duration, and traffic sources in your GA4 dashboard.
Step 5: Track Custom Events
Basic page views are fine, but the real value is in custom event tracking. Track when users submit forms, click CTAs, or interact with key features.
Create a utility function:
// src/lib/analytics.ts
export function trackEvent(eventName: string, params?: Record<string, string>) {
if (typeof window !== "undefined" && window.gtag) {
window.gtag("event", eventName, params);
}
}Use it anywhere in your components:
import { trackEvent } from "@/lib/analytics";
// Track contact form submission
const handleSubmit = async () => {
await submitForm(formData);
trackEvent("contact_form_submit", {
form_location: "homepage",
});
};
// Track CTA button click
<button onClick={() => trackEvent("cta_click", { button: "hero_get_started" })}>
Get Started
</button>You'll see these events in GA4 under Reports → Engagement → Events.
Step 6: Add TypeScript Types for gtag
To avoid TypeScript errors, add the type declaration:
// src/types/gtag.d.ts
interface Window {
gtag: (
command: string,
targetId: string,
params?: Record<string, unknown>
) => void;
dataLayer: unknown[];
fbq: (...args: unknown[]) => void;
}This file covers types for both gtag and fbq (Meta Pixel) — we'll use fbq later.
Part 2: Google Ads Conversion Tracking
GA4 tells you what visitors do on your site. Google Ads conversion tracking tells you which ad click led to that action. Without this, you're flying blind on ad spend.
Step 1: Create a Conversion Action in Google Ads
- Go to Google Ads
- Click Goals → Conversions → New Conversion Action
- Select Website
- Choose the conversion type (form submission, purchase, signup, etc.)
- Note down the Conversion ID (looks like
AW-XXXXXXXXXX) and Conversion Label
Step 2: Add Google Ads Tag
The good news — if you already set up GA4 with gtag.js, you already have the Google Tag loaded. You just need to add the Google Ads config.
Update your GoogleAnalytics component:
// src/components/GoogleAnalytics.tsx
"use client";
import Script from "next/script";
const GA_MEASUREMENT_ID = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID;
const GOOGLE_ADS_ID = process.env.NEXT_PUBLIC_GOOGLE_ADS_ID;
export default function GoogleAnalytics() {
if (!GA_MEASUREMENT_ID) return null;
return (
<>
<Script
src={`https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`}
strategy="afterInteractive"
/>
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GA_MEASUREMENT_ID}');
${GOOGLE_ADS_ID ? `gtag('config', '${GOOGLE_ADS_ID}');` : ""}
`}
</Script>
</>
);
}Step 3: Fire Conversion Events
When a user completes a valuable action (submits a form, makes a purchase), fire the conversion event:
// src/lib/analytics.ts
export function trackEvent(eventName: string, params?: Record<string, string>) {
if (typeof window !== "undefined" && window.gtag) {
window.gtag("event", eventName, params);
}
}
export function trackGoogleAdsConversion(conversionLabel: string) {
const adsId = process.env.NEXT_PUBLIC_GOOGLE_ADS_ID;
if (typeof window !== "undefined" && window.gtag && adsId) {
window.gtag("event", "conversion", {
send_to: `${adsId}/${conversionLabel}`,
});
}
}Use it when a form is submitted:
import { trackEvent, trackGoogleAdsConversion } from "@/lib/analytics";
const handleContactSubmit = async () => {
const response = await fetch("/api/contacts", {
method: "POST",
body: JSON.stringify(formData),
});
if (response.ok) {
// Track in GA4
trackEvent("contact_form_submit");
// Track Google Ads conversion
trackGoogleAdsConversion("YOUR_CONVERSION_LABEL");
}
};Step 4: Add Environment Variable
NEXT_PUBLIC_GOOGLE_ADS_ID=AW-XXXXXXXXXXStep 5: Verify in Google Ads
- Go to Goals → Conversions in Google Ads
- You should see your conversion action listed
- Use the Google Tag Assistant Chrome extension to verify the tag fires correctly
- Make a test conversion on your site and check if it appears in Google Ads (may take a few hours)
Part 3: Meta Pixel (Facebook/Instagram Ads) Setup
Meta Pixel tracks visitors from Facebook and Instagram ads. It also lets you build retargeting audiences — show ads to people who visited your site but didn't convert. This is where the real ROI is.
Step 1: Create Your Meta Pixel
- Go to Meta Events Manager
- Click Connect Data Sources → Web → Meta Pixel
- Name your pixel and enter your website URL
- Copy the Pixel ID (a number like
123456789012345)
Step 2: Create the Meta Pixel Component
// src/components/MetaPixel.tsx
"use client";
import Script from "next/script";
const META_PIXEL_ID = process.env.NEXT_PUBLIC_META_PIXEL_ID;
export default function MetaPixel() {
if (!META_PIXEL_ID) return null;
return (
<>
<Script id="meta-pixel" strategy="afterInteractive">
{`
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', '${META_PIXEL_ID}');
fbq('track', 'PageView');
`}
</Script>
<noscript>
<img
height="1"
width="1"
style={{ display: "none" }}
src={`https://www.facebook.com/tr?id=${META_PIXEL_ID}&ev=PageView&noscript=1`}
alt=""
/>
</noscript>
</>
);
}Step 3: Add to Root Layout
// src/app/layout.tsx
import GoogleAnalytics from "@/components/GoogleAnalytics";
import MetaPixel from "@/components/MetaPixel";
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<GoogleAnalytics />
<MetaPixel />
{children}
</body>
</html>
);
}Step 4: Track Meta Pixel Events
Meta Pixel has standard events that Meta uses to optimize ad delivery. The more events you track, the better Meta can find customers similar to your converters.
Add Meta Pixel tracking to your analytics utility:
// src/lib/analytics.ts
// ... existing GA4 and Google Ads functions ...
export function trackMetaEvent(eventName: string, params?: Record<string, unknown>) {
if (typeof window !== "undefined" && window.fbq) {
window.fbq("track", eventName, params);
}
}Standard events you should track:
// Contact form submitted
trackMetaEvent("Lead", {
content_name: "Contact Form",
});
// User signed up
trackMetaEvent("CompleteRegistration", {
content_name: "Signup Form",
});
// User viewed a key page (pricing, services)
trackMetaEvent("ViewContent", {
content_name: "Pricing Page",
content_category: "Services",
});
// Purchase completed (for e-commerce)
trackMetaEvent("Purchase", {
value: 99.00,
currency: "INR",
});Step 5: Add Environment Variable
NEXT_PUBLIC_META_PIXEL_ID=123456789012345Step 6: Verify With Meta Pixel Helper
- Install the Meta Pixel Helper Chrome extension
- Visit your website
- The extension icon should show a green checkmark with the number of events fired
- Go to Events Manager → Test Events in Meta Business Suite to see real-time event data
Putting It All Together
Here's what your final setup looks like — a single contact form submission that tracks across all three platforms:
import { trackEvent, trackGoogleAdsConversion, trackMetaEvent } from "@/lib/analytics";
const handleSubmit = async (formData: FormData) => {
const response = await fetch("/api/contacts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: formData.get("name"),
phone: formData.get("phone"),
message: formData.get("message"),
}),
});
if (response.ok) {
// GA4 — track the event for behavior analysis
trackEvent("contact_form_submit", {
form_location: "homepage",
});
// Google Ads — track conversion for ad optimization
trackGoogleAdsConversion("YOUR_CONVERSION_LABEL");
// Meta Pixel — track lead for Facebook/Instagram ad optimization
trackMetaEvent("Lead", {
content_name: "Contact Form",
});
}
};The Complete Analytics Utility File
Here's the final analytics.ts file with all tracking functions in one place:
// src/lib/analytics.ts
// GA4 custom event tracking
export function trackEvent(eventName: string, params?: Record<string, string>) {
if (typeof window !== "undefined" && window.gtag) {
window.gtag("event", eventName, params);
}
}
// Google Ads conversion tracking
export function trackGoogleAdsConversion(conversionLabel: string) {
const adsId = process.env.NEXT_PUBLIC_GOOGLE_ADS_ID;
if (typeof window !== "undefined" && window.gtag && adsId) {
window.gtag("event", "conversion", {
send_to: `${adsId}/${conversionLabel}`,
});
}
}
// Meta Pixel event tracking
export function trackMetaEvent(eventName: string, params?: Record<string, unknown>) {
if (typeof window !== "undefined" && window.fbq) {
window.fbq("track", eventName, params);
}
}Environment Variables Checklist
Your .env.local should have:
# Google Analytics 4
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX
# Google Ads
NEXT_PUBLIC_GOOGLE_ADS_ID=AW-XXXXXXXXXX
# Meta Pixel
NEXT_PUBLIC_META_PIXEL_ID=123456789012345Important: The NEXT_PUBLIC_ prefix is required. Without it, Next.js won't expose the variable to client-side code, and your tracking scripts won't work.
Common Mistakes to Avoid
1. Loading Scripts With beforeInteractive
Don't use strategy="beforeInteractive" for tracking scripts. It blocks page rendering and hurts your Core Web Vitals. Always use afterInteractive — tracking a page view 200ms later is fine, a slow website is not.
2. Not Testing on Production
Tracking scripts often behave differently in development vs production. Always verify your setup on the live site using:
- Google Tag Assistant (for GA4 and Google Ads)
- Meta Pixel Helper (for Meta Pixel)
- GA4 Real-time report (Analytics → Reports → Real-time)
3. Forgetting the noscript Fallback for Meta Pixel
The <noscript> image tag in the Meta Pixel component is not optional. It tracks users who have JavaScript disabled, and Meta uses it for verification.
4. Firing Conversions on Page Load Instead of Actions
Never fire conversion events on page load. Only fire them when the user actually completes the action (form submit success, purchase confirmation). Otherwise, your conversion data will be inflated and useless.
5. Not Filtering Internal Traffic
In GA4, go to Admin → Data Streams → Configure Tag Settings → Define Internal Traffic and add your IP address. This prevents your own visits from polluting the data.
What to Do After Setup
Once tracking is live, here's how to use the data:
- Wait 48-72 hours for enough data to accumulate
- Check GA4 — verify page views, user flow, and custom events are recording
- Check Google Ads — verify conversions are being attributed to the correct campaigns
- Check Meta Events Manager — verify pixel events and build your first retargeting audience
- Create a retargeting audience in Meta — target people who visited your site in the last 30 days but didn't submit the contact form
- Set up GA4 conversions — mark your key events (form submissions, signups) as conversions in GA4 for easier reporting
Need Help Setting This Up?
I set up analytics and ad tracking on every website I build for clients. If you need a website with proper tracking, SEO optimization, and conversion-focused design — let's connect.