Google Analytics & Google Tag Manager¶
Breadcrumbs: Documentation > Guides > SEO > Analytics
This guide explains the Google Analytics and Google Tag Manager implementation for tracking website metrics.
Table of Contents¶
- Overview
- Google Analytics Setup
- Google Tag Manager Setup
- Implementation
- Event Tracking
- Privacy Considerations
- Testing
Overview¶
The portfolio uses both Google Analytics 4 (GA4) and Google Tag Manager (GTM) for comprehensive analytics tracking.
Features:
- Page view tracking
- Event tracking (downloads, clicks, etc.)
- User behavior analysis
- Conversion tracking
- Real-time monitoring
Additional analytics:
- Vercel Analytics (built-in)
- Vercel Speed Insights (performance monitoring)
Google Analytics Setup¶
Create GA4 Property¶
Steps:
- Go to Google Analytics
- Click "Admin" (gear icon)
- Click "Create Property"
- Enter property details:
- Property name: "Portfolio Website"
- Time zone: Your timezone
- Currency: Your currency
- Click "Next"
- Fill business information
- Click "Create"
- Accept terms
- Copy Measurement ID (format:
G-XXXXXXXXXX)
Configure Data Streams¶
- Click "Data Streams"
- Click "Add stream" → "Web"
- Enter website URL
- Enter stream name
- Click "Create stream"
- Copy Measurement ID
Add to Environment Variables¶
Google Tag Manager Setup¶
Create GTM Account¶
Steps:
- Go to Google Tag Manager
- Click "Create Account"
- Enter account name (your name/company)
- Enter container name (your website domain)
- Select "Web" as target platform
- Click "Create"
- Accept terms
- Copy Container ID (format:
GTM-XXXXXXX)
Add to Environment Variables¶
Install GTM Container Code¶
The code is automatically installed via the Analytics component.
Implementation¶
Analytics Component¶
File: components/meta/analytics.tsx
// components/meta/analytics.tsx
import { analyticsConfig, appConfig } from '@/lib/config';
export function Analytics() {
// Don't render in development
if (!analyticsConfig.gaId || !analyticsConfig.gtmId || appConfig.isDevelopment) {
return null;
}
return (
<>
{/* Google Site Verification */}
<meta name="google-site-verification" content="your-verification-code" />
{/* Google Analytics */}
<script
async
src={`https://www.googletagmanager.com/gtag/js?id=${analyticsConfig.gaId}`}
/>
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${analyticsConfig.gaId}';
`,
}}
/>
{/* Google Tag Manager */}
<script
dangerouslySetInnerHTML={{
__html: `
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','${analyticsConfig.gtmId}');
`,
}}
/>
</>
);
}
Layout Integration¶
// app/layout.tsx
import Analytics from '@/components/meta/analytics';
import { SpeedInsights } from '@vercel/speed-insights/next';
import { Analytics as VercelAnalytics } from '@vercel/analytics/next';
export default function RootLayout({ children }) {
return (
<html lang="en">
<head>
<Analytics />
<VercelAnalytics />
<SpeedInsights />
</head>
<body>
{/* GTM noscript fallback */}
<noscript>
<iframe
src={`https://www.googletagmanager.com/ns.html?id=${analyticsConfig.gtmId}`}
height="0"
width="0"
style={{ display: 'none', visibility: 'hidden' }}
/>
</noscript>
{children}
</body>
</html>
);
}
Configuration¶
// lib/config.ts
export const analyticsConfig = {
gaId: process.env.NEXT_PUBLIC_GA_ID || "",
gtmId: process.env.NEXT_PUBLIC_GTM_ID || "",
};
export const appConfig = {
environment: process.env.NODE_ENV || "development",
isDevelopment: process.env.NODE_ENV === "development",
isProduction: process.env.NODE_ENV === "production",
};
Event Tracking¶
Custom Events¶
Track button clicks:
// components/download-button.tsx
'use client';
export function DownloadButton() {
const handleDownload = () => {
// Track with Google Analytics
if (typeof window !== 'undefined' && window.gtag) {
window.gtag('event', 'download', {
event_category: 'engagement',
event_label: 'resume',
value: 1,
});
}
// Download logic
window.location.href = '/download/resume.pdf';
};
return <Button onClick={handleDownload}>Download Resume</Button>;
}
Track external links:
// components/external-link.tsx
'use client';
export function ExternalLink({ href, children }) {
const handleClick = () => {
if (typeof window !== 'undefined' && window.gtag) {
window.gtag('event', 'click', {
event_category: 'outbound',
event_label: href,
transport_type: 'beacon',
});
}
};
return (
<a href={href} target="_blank" rel="noopener noreferrer" onClick={handleClick}>
{children}
</a>
);
}
GTM Custom Events¶
Using Data Layer:
'use client';
export function ContactForm() {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
// Push event to GTM Data Layer
if (typeof window !== 'undefined' && window.dataLayer) {
window.dataLayer.push({
event: 'form_submission',
formName: 'contact_form',
formId: 'contact',
});
}
// Form submission logic
};
return <form onSubmit={handleSubmit}>{/* Form fields */}</form>;
}
TypeScript declarations:
// global.d.ts
declare global {
interface Window {
gtag: (...args: any[]) => void;
dataLayer: any[];
}
}
export {};
Common Events to Track¶
1. Page views (automatic with GA4)
2. Downloads:
gtag("event", "file_download", {
file_name: "resume.pdf",
file_extension: "pdf",
link_url: "/download/resume.pdf",
});
3. Outbound clicks:
gtag("event", "click", {
event_category: "outbound",
event_label: "https://github.com/username",
});
4. Project views:
gtag("event", "view_item", {
item_id: project.slug,
item_name: project.title,
item_category: "project",
});
5. Social clicks:
Privacy Considerations¶
GDPR Compliance¶
If targeting EU users, consider:
- Cookie consent banner
- Privacy policy page
- Data processing agreement
- User opt-out option
Cookie Consent¶
Basic implementation:
'use client';
import { useState, useEffect } from 'react';
export function CookieConsent() {
const [showBanner, setShowBanner] = useState(false);
useEffect(() => {
const consent = localStorage.getItem('cookie-consent');
if (!consent) {
setShowBanner(true);
}
}, []);
const acceptCookies = () => {
localStorage.setItem('cookie-consent', 'accepted');
setShowBanner(false);
// Initialize analytics
if (window.gtag) {
window.gtag('consent', 'update', {
analytics_storage: 'granted',
});
}
};
if (!showBanner) return null;
return (
<div className="fixed bottom-0 left-0 right-0 bg-card p-4 shadow-lg">
<div className="container mx-auto flex items-center justify-between">
<p>We use cookies to improve your experience.</p>
<Button onClick={acceptCookies}>Accept</Button>
</div>
</div>
);
}
Anonymize IP Addresses¶
GA4 (automatic in GA4):
Opt-Out¶
Provide opt-out option:
'use client';
export function OptOutButton() {
const handleOptOut = () => {
// Disable Google Analytics
if (typeof window !== 'undefined') {
window[`ga-disable-${analyticsConfig.gaId}`] = true;
localStorage.setItem('analytics-opt-out', 'true');
alert('Analytics disabled');
}
};
return <Button onClick={handleOptOut}>Opt Out of Analytics</Button>;
}
Testing¶
Test in Development¶
1. Enable in development (temporarily):
// components/meta/analytics.tsx
export function Analytics() {
// Remove isDevelopment check for testing
if (!analyticsConfig.gaId || !analyticsConfig.gtmId) {
return null;
}
// Rest of code...
}
2. Check browser console:
Test with GA Debugger¶
Chrome Extension:
- Install Google Analytics Debugger
- Enable extension
- Visit your site
- Check console for GA debug messages
Test with GTM Preview¶
Steps:
- Go to Google Tag Manager
- Click "Preview"
- Enter your website URL
- Click "Connect"
- Interact with your site
- Check which tags fire
Test in Production¶
Real-time reports:
- Go to Google Analytics
- Click "Reports" → "Realtime"
- Visit your site in another tab
- Check if visit appears
Common Issues¶
Issue: Analytics not loading¶
Check:
- Environment variables set correctly
- Not in development mode
- Ad blocker disabled
- Console for errors
Issue: Events not tracking¶
Check:
gtagfunction exists- Event name and parameters correct
- GTM tags configured properly
- Check GA4 DebugView
See Also¶
- SEO Guide - Complete SEO implementation
- Performance - Performance optimization
- Configuration - Environment variables
Next Steps¶
- Create GA4 property
- Create GTM container
- Add IDs to environment variables
- Test analytics tracking
- Set up custom events
- Monitor analytics reports
Last Updated: February 2026
Maintainers: Simon Stijnen
Questions? Open an issue on GitHub