The boilerplate ships with dual instrumentation: Google Analytics 4 for page views/funnels and Mixpanel for product events. This page explains how to enable both.
For detailed event docs and tracking examples, see the Analytics section.
src/app/
├── components/
│ └── analytics/
│ ├── GoogleAnalytics.tsx # GA4 script
│ └── MixpanelProvider.tsx # Mixpanel provider
├── lib/
│ ├── analytics/
│ │ ├── ga.ts # GA4 helper functions
│ │ ├── mixpanel.ts # Mixpanel helper functions
│ │ └── index.ts # Unified exports
│ └── hooks/
│ └── useAnalytics.ts # Tracking hook
└── app/
└── layout.tsx # Providers configured# Google Analytics 4
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX
# Mixpanel
NEXT_PUBLIC_MIXPANEL_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Environment (disables tracking in dev)
NEXT_PUBLIC_APP_ENV=developmentG-XXXXXXXXXX)The GoogleAnalytics component is already wired inside the layout:
import { GoogleAnalytics } from '@/components/analytics/GoogleAnalytics'
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<GoogleAnalytics />
</body>
</html>
)
}'use client'
import Script from 'next/script'
export function GoogleAnalytics() {
const gaId = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID
if (!gaId || process.env.NEXT_PUBLIC_APP_ENV === 'development') {
return null
}
return (
<>
<Script
src={`https://www.googletagmanager.com/gtag/js?id=${gaId}`}
strategy="afterInteractive"
/>
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${gaId}', {
page_path: window.location.pathname,
});
`}
</Script>
</>
)
}type GAEvent = {
action: string
category: string
label?: string
value?: number
}
export function trackEvent({ action, category, label, value }: GAEvent) {
if (typeof window === 'undefined' || !window.gtag) return
window.gtag('event', action, {
event_category: category,
event_label: label,
value: value,
})
}
export function trackPageView(url: string) {
if (typeof window === 'undefined' || !window.gtag) return
window.gtag('config', process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID!, {
page_path: url,
})
}import { MixpanelProvider } from '@/components/analytics/MixpanelProvider'
export default function RootLayout({ children }) {
return (
<html>
<body>
<MixpanelProvider>
{children}
</MixpanelProvider>
</body>
</html>
)
}'use client'
import { useEffect } from 'react'
import mixpanel from 'mixpanel-browser'
export function MixpanelProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
const token = process.env.NEXT_PUBLIC_MIXPANEL_TOKEN
if (token && process.env.NEXT_PUBLIC_APP_ENV !== 'development') {
mixpanel.init(token, {
debug: false,
track_pageview: true,
persistence: 'localStorage',
})
}
}, [])
return <>{children}</>
}import mixpanel from 'mixpanel-browser'
export function trackMixpanelEvent(
event: string,
properties?: Record<string, unknown>
) {
if (process.env.NEXT_PUBLIC_APP_ENV === 'development') {
console.log('[Mixpanel]', event, properties)
return
}
mixpanel.track(event, properties)
}
export function identifyUser(userId: string, traits?: Record<string, unknown>) {
if (process.env.NEXT_PUBLIC_APP_ENV === 'development') return
mixpanel.identify(userId)
if (traits) {
mixpanel.people.set(traits)
}
}
export function resetUser() {
if (process.env.NEXT_PUBLIC_APP_ENV === 'development') return
mixpanel.reset()
}Use the useAnalytics hook to simplify usage:
'use client'
import { useCallback } from 'react'
import { trackEvent as trackGA } from '@/lib/analytics/ga'
import { trackMixpanelEvent } from '@/lib/analytics/mixpanel'
interface TrackEventParams {
name: string
category?: string
properties?: Record<string, unknown>
}
export function useAnalytics() {
const track = useCallback(({ name, category, properties }: TrackEventParams) => {
// GA4
trackGA({
action: name,
category: category || 'general',
label: properties?.label as string,
})
// Mixpanel
trackMixpanelEvent(name, {
category,
...properties,
})
}, [])
return { track }
}'use client'
import { useAnalytics } from '@/lib/hooks/useAnalytics'
export function BuyButton({ productId, price }: Props) {
const { track } = useAnalytics()
function handleClick() {
track({
name: 'purchase_click',
category: 'ecommerce',
properties: {
productId,
price,
currency: 'BRL',
},
})
}
return <button onClick={handleClick}>Buy</button>
}| Event | When | Properties |
|---|---|---|
view_item | Product page | item_id, item_name, price |
add_to_cart | Add to cart | item_id, quantity, price |
begin_checkout | Start checkout | items, value |
purchase | Purchase completed | transaction_id, value, items |
| Event | When | Properties |
|---|---|---|
sign_up | Sign up | method |
login | Login | method |
page_view | Page view | page_path, page_title |
click | Key clicks | element, destination |
Install the Google Analytics Debugger extension and enable it.
// In development the events are logged in the console
if (process.env.NEXT_PUBLIC_APP_ENV === 'development') {
console.log('[Mixpanel]', event, properties)
}NEXT_PUBLIC_GA_MEASUREMENT_ID is correctdevelopment.env.local