📋 Analytics Implementation Task Guide
This document explains **exactly how** the analytics system was implemented so you can replicate it in any project.
📋 Analytics Implementation Task Guide
Overview
This document explains exactly how the analytics system was implemented so you can replicate it in any project.
🎯 Goal
Implement PostHog analytics to track client usage even after sharing code. All events flow back to YOUR analytics instance.
📦 Step 1: Install Dependencies
npm install posthog-js
What it does: Installs PostHog JavaScript SDK for client-side tracking.
🏗️ Step 2: Create Core Analytics Service
File: lib/analytics.ts
This is the heart of the system - a singleton service that wraps PostHog.
Key Components:
A. Type-Safe Event Definitions
export type AnalyticsEvent = {
'research_initiated': {
query: string
model: string
criteriaCount: number
userId?: string
}
'research_completed': {
query: string
model: string
duration: number
success: boolean
resultsCount: number
userId?: string
}
// ... add all your events here
}
Why: TypeScript ensures you never misspell event names or properties.
B. Analytics Class
class Analytics {
private initialized = false
private userId: string | null = null
// Initialize PostHog
initialize(apiKey: string, options?: { host?: string }) {
if (this.initialized) return
if (typeof window === 'undefined') return // Server-side check
posthog.init(apiKey, {
api_host: options?.host || 'https://app.posthog.com',
capture_pageview: true,
autocapture: true,
session_recording: {
maskAllInputs: true,
maskTextSelector: '*',
},
})
this.initialized = true
}
// Track events
track<K extends keyof AnalyticsEvent>(
event: K,
properties: AnalyticsEvent[K]
) {
if (!this.initialized) return
posthog.capture(event, {
...properties,
timestamp: new Date().toISOString(),
environment: process.env.NODE_ENV,
userId: this.userId || properties.userId || 'anonymous',
})
}
// Identify users
identify(userId: string, traits?: Record<string, any>) {
if (!this.initialized) return
this.userId = userId
posthog.identify(userId, traits)
}
// Timer utility
startTimer(operationName: string): () => number {
const startTime = performance.now()
return () => Math.round(performance.now() - startTime)
}
// Feature flags
isFeatureEnabled(key: string): boolean {
if (!this.initialized) return false
return posthog.isFeatureEnabled(key) ?? false
}
}
// Export singleton
export const analytics = new Analytics()
C. Server-Side Tracking Function
export async function trackServerEvent<K extends keyof AnalyticsEvent>(
event: K,
properties: AnalyticsEvent[K],
apiKey?: string
) {
if (!apiKey) return
await fetch('https://app.posthog.com/capture/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key: apiKey,
event,
properties: {
...properties,
timestamp: new Date().toISOString(),
environment: process.env.NODE_ENV,
},
timestamp: new Date().toISOString(),
}),
})
}
Why: API routes run server-side and need direct HTTP calls to PostHog.
🔌 Step 3: Create React Provider
File: components/analytics-provider-wrapper.tsx
Initializes analytics when the app loads and identifies users automatically.
"use client"
import { useEffect } from 'react'
import { useSession } from 'next-auth/react'
import { analytics } from '@/lib/analytics'
export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
const { data: session, status } = useSession()
// Initialize on mount
useEffect(() => {
const apiKey = process.env.NEXT_PUBLIC_POSTHOG_KEY
const host = process.env.NEXT_PUBLIC_POSTHOG_HOST
if (apiKey) {
analytics.initialize(apiKey, { host })
}
}, [])
// Identify user when logged in
useEffect(() => {
if (status === 'authenticated' && session?.user) {
analytics.identify(session.user.email || 'unknown', {
email: session.user.email,
name: session.user.name,
})
} else if (status === 'unauthenticated') {
analytics.reset()
}
}, [session, status])
return <>{children}</>
}
Key Points:
- Runs only on client (
"use client") - Initializes once on app load
- Auto-identifies authenticated users
- Resets on logout
🎨 Step 4: Integrate Provider in Layout
File: app/layout.tsx
Wrap your entire app with the analytics provider.
import { AnalyticsProvider } from '@/components/analytics-provider-wrapper'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<AuthProvider>
<AnalyticsProvider>
{children}
</AnalyticsProvider>
</AuthProvider>
</body>
</html>
)
}
Why: Ensures analytics is initialized before any component renders.
🪝 Step 5: Create React Hook (Optional)
File: lib/hooks/useAnalytics.ts
Makes it easier to track events in components.
import { useCallback } from 'react'
import { analytics, type AnalyticsEvent } from '@/lib/analytics'
export function useAnalytics() {
const track = useCallback(<K extends keyof AnalyticsEvent>(
event: K,
properties: AnalyticsEvent[K]
) => {
analytics.track(event, properties)
}, [])
const startTimer = useCallback((operationName: string) => {
return analytics.startTimer(operationName)
}, [])
return { track, startTimer, identify: analytics.identify.bind(analytics) }
}
Usage in components:
const { track, startTimer } = useAnalytics()
const handleClick = () => {
track('button_clicked', { buttonName: 'submit' })
}
🔧 Step 6: Add Tracking to API Routes
Example: app/api/research/route.ts
Import the tracking function:
import { trackServerEvent } from '@/lib/analytics'
Track on success:
export const POST = async (req: Request) => {
try {
const body = await req.json()
const { city, model, criteria } = body
// Track event
await trackServerEvent('research_initiated', {
query: city,
model: model || 'default',
criteriaCount: criteria?.length || 0,
}, process.env.NEXT_PUBLIC_POSTHOG_KEY)
// ... your logic here ...
return NextResponse.json({ success: true })
} catch (error) {
// Track errors
await trackServerEvent('api_error', {
endpoint: '/api/research',
errorMessage: error instanceof Error ? error.message : 'Unknown error',
statusCode: 500,
}, process.env.NEXT_PUBLIC_POSTHOG_KEY)
return NextResponse.json({ error: 'Failed' }, { status: 500 })
}
}
Pattern:
- Import
trackServerEvent - Track success events with relevant data
- Track errors in catch blocks
- Always pass
process.env.NEXT_PUBLIC_POSTHOG_KEY
🎯 Step 7: Add Tracking to Components
Example: components/research-panel.tsx
Import analytics:
import { analytics } from '@/lib/analytics'
Track user interactions:
const handleSearch = async () => {
// Track search initiated
analytics.track('research_initiated', {
query: cityInput,
model: model,
criteriaCount: criteria.length,
})
// Time the operation
const endTimer = analytics.startTimer('search_operation')
try {
const response = await fetch('/api/research', { /* ... */ })
const duration = endTimer()
// Track success
analytics.track('research_completed', {
query: cityInput,
model: model,
duration,
success: true,
resultsCount: data.results?.length || 0,
})
} catch (error) {
// Track failure
analytics.track('research_failed', {
query: cityInput,
model: model,
error: error.message,
})
}
}
Best Practices:
- Track at the START of operations
- Track at the END (success/failure)
- Use timers for duration tracking
- Include context (query, model, etc.)
🌍 Step 8: Environment Configuration
File: .env.local (create this, never commit)
NEXT_PUBLIC_POSTHOG_KEY=phc_your_actual_key_here
NEXT_PUBLIC_POSTHOG_HOST=https://app.posthog.com
File: .env.example (commit this)
# Analytics Configuration (PostHog)
# Sign up at https://posthog.com or self-host
# Get your API key and paste below
NEXT_PUBLIC_POSTHOG_KEY=
NEXT_PUBLIC_POSTHOG_HOST=https://app.posthog.com
Why NEXT_PUBLIC_?
- Prefix makes it available in browser
- Next.js bakes it into the build
- Client code can access it
🧪 Step 9: Create Test Utilities
File: lib/analytics-test.ts
export const quickVerify = () => {
const results = {
posthogLoaded: typeof window !== 'undefined' && !!(window as any).posthog,
posthogInitialized: !!(window as any).posthog?.__loaded,
apiKeyPresent: !!process.env.NEXT_PUBLIC_POSTHOG_KEY,
trackingWorks: false,
}
try {
analytics.track('test_event', { test: true } as any)
results.trackingWorks = true
} catch {
results.trackingWorks = false
}
console.table(results)
return results
}
Usage: Run in browser console to verify setup.
📊 Step 10: Enhance Analytics Dashboard
File: components/analytics-logs.tsx
Show real-time analytics info to users (optional).
"use client"
import { useEffect, useState } from 'react'
import posthog from 'posthog-js'
export function AnalyticsLogs() {
const [logs, setLogs] = useState<any[]>([])
useEffect(() => {
// Check if PostHog is initialized
const isInitialized = typeof window !== 'undefined' && posthog.__loaded
if (isInitialized) {
setLogs([{
message: 'Analytics tracking active',
type: 'INFO'
}])
} else {
setLogs([{
message: 'Configure PostHog in .env.local',
type: 'WARNING'
}])
}
}, [])
return (
<div>
{/* Display logs */}
</div>
)
}
🔒 Step 11: Security & Deployment
For Client Deployment:
- Keep analytics configured - Don't remove PostHog code
- Use YOUR API key - Bake it into the build
- Deploy normally -
npm run build && npm start - Monitor immediately - Events flow to YOUR dashboard
Environment Variable Strategy:
# Local Development (.env.local - gitignored)
NEXT_PUBLIC_POSTHOG_KEY=phc_your_dev_key
# Production (Vercel/Netlify/etc)
NEXT_PUBLIC_POSTHOG_KEY=phc_your_prod_key
# Client's Environment (they never see this)
# Key is baked into build at deploy time
📋 Complete Event List
Add these to your AnalyticsEvent type:
export type AnalyticsEvent = {
// Research
'research_initiated': { query: string; model: string; criteriaCount: number }
'research_completed': { query: string; duration: number; success: boolean }
'research_failed': { query: string; error: string }
// Model
'model_changed': { previousModel: string; newModel: string }
'model_settings_updated': { model: string; settings: any }
// Criteria
'custom_criteria_added': { criteriaName: string; criteriaType: string }
'custom_criteria_removed': { criteriaName: string }
// Auth
'user_signed_in': { userId: string; method: string }
'user_signed_up': { userId: string }
'user_signed_out': { userId: string }
// Errors
'api_error': { endpoint: string; errorMessage: string; statusCode?: number }
'client_error': { component: string; errorMessage: string }
// Performance
'api_response_time': { endpoint: string; duration: number; success: boolean }
'page_load': { page: string; loadTime: number }
}
🎯 Usage Patterns
Pattern 1: Track Simple Event
analytics.track('button_clicked', {
buttonName: 'submit',
userId: currentUser.id,
})
Pattern 2: Track with Duration
const endTimer = analytics.startTimer('operation')
await doSomething()
const duration = endTimer()
analytics.track('operation_completed', {
operation: 'data_fetch',
duration,
success: true,
})
Pattern 3: Track Error
try {
await riskyOperation()
} catch (error) {
analytics.track('operation_failed', {
operation: 'data_fetch',
error: error.message,
})
}
Pattern 4: Server-Side Tracking
// In API route
await trackServerEvent('api_called', {
endpoint: '/api/endpoint',
method: 'POST',
}, process.env.NEXT_PUBLIC_POSTHOG_KEY)
🚀 Quick Setup Checklist
When implementing in a new project:
- Install
posthog-js - Create
lib/analytics.tswith Analytics class - Create
components/analytics-provider-wrapper.tsx - Add provider to
app/layout.tsx - Create
lib/hooks/useAnalytics.ts(optional) - Add tracking to API routes
- Add tracking to components
- Create
.env.localwith PostHog key - Create
.env.exampletemplate - Test with
quickVerify()function - Deploy and verify events in PostHog
🔍 Debugging
Analytics not working?
-
Check initialization:
// In browser console console.log(window.posthog) console.log(window.posthog.__loaded) -
Check environment variable:
console.log(process.env.NEXT_PUBLIC_POSTHOG_KEY) -
Check network requests:
- Open DevTools → Network
- Look for
app.posthog.com/capture/ - Should see 200 OK responses
-
Run verification:
import { quickVerify } from '@/lib/analytics-test' quickVerify()
📚 PostHog Setup
Option 1: Cloud (5 minutes)
- Go to https://posthog.com/signup
- Create account
- Create new project
- Copy API key (starts with
phc_) - Paste in
.env.local
Option 2: Self-Hosted (30 minutes)
# Clone PostHog
git clone https://github.com/PostHog/posthog.git
cd posthog
# Start with Docker
docker-compose up -d
# Access at http://localhost:8000
# Create project and get API key
Self-Hosted Benefits:
- Complete data ownership
- No external dependencies
- Maximum privacy compliance
- Unlimited events
🎯 Key Concepts
Why This Works for Client Tracking:
- API key in build:
NEXT_PUBLIC_variables are baked into JavaScript bundle - Your instance: Events always go to YOUR PostHog (your API key)
- Client deploys: They deploy the built code with your key embedded
- You monitor: All their events flow to your dashboard
Architecture:
Client's Deployment
↓
Your Built Code (with your PostHog key)
↓
User Actions → Browser → PostHog.capture()
↓
YOUR PostHog Instance (Cloud or Self-Hosted)
↓
Your Analytics Dashboard
🔄 Replication Steps
To replicate in any project:
-
Copy Files:
lib/analytics.tscomponents/analytics-provider-wrapper.tsxlib/hooks/useAnalytics.tslib/analytics-test.ts
-
Install Package:
npm install posthog-js -
Integrate:
- Wrap app with
<AnalyticsProvider> - Add tracking to API routes
- Add tracking to components
- Wrap app with
-
Configure:
- Get PostHog API key
- Add to
.env.local - Test with
quickVerify()
-
Deploy:
- Build with
npm run build - Deploy to production
- Monitor events in PostHog
- Build with
✅ Success Criteria
You've successfully implemented analytics when:
- ✅
quickVerify()shows all green checks - ✅ Events appear in PostHog dashboard within 60 seconds
- ✅ Session replays are recording
- ✅ No console errors related to PostHog
- ✅ Network tab shows successful capture requests
- ✅ User identification works after login
🎓 Advanced Features
Feature Flags:
if (analytics.isFeatureEnabled('new-ui')) {
// Show new UI
}
A/B Testing:
const variant = analytics.getFeatureFlag('pricing-test')
// Returns 'control' or 'test'
Cohorts:
Create user groups in PostHog dashboard based on behavior.
Custom Dashboards:
Build dashboards in PostHog with:
- Total events by type
- User engagement metrics
- Performance trends
- Error rates
📝 Final Notes
Remember:
- This implementation is production-ready
- It scales to millions of events
- Self-hosting gives you complete control
- Client deployments automatically track to YOUR instance
- No additional client configuration needed
Cost:
- PostHog Cloud: Free tier → 1M events/month
- Self-Hosted: Only infrastructure costs (AWS, DigitalOcean, etc.)
Privacy:
- GDPR compliant
- Automatic PII masking
- Data retention controls
- User opt-out support
🎉 Done!
You now have a complete blueprint for implementing analytics in any Next.js project. Just follow this guide step-by-step and you'll have enterprise-grade tracking in under an hour.
Key Takeaway: Once implemented, you maintain complete visibility into how clients use your code, even after deployment!
Related Documents
ClockRoss - AI-Powered Analog Clock
A Python-based analog clock application that combines real-time clock display with AI-generated backgrounds using local Stable Diffusion via Diffusers, enhanced with ControlNet and GPT-2 prompt generation. Supports both NVIDIA (CUDA) and Apple Silicon (MPS) hardware acceleration.
CMU Advanced NLP Assignment 2: End-to-end NLP System Building
Large language models (LLMs) such as Llama2 have been shown effective for question-answering ([Touvron et al., 2023](https://arxiv.org/abs/2307.09288)), however, they are often limited by their knowledge in certain domains. A common technique here is to augment LLM's knowledge with documents that are relevant to the question. In this assignment, you will *develop a retrieval augmented generation system (RAG)* ([Lewis et al., 2021](https://arxiv.org/abs/2005.11401)) that's capable of answering qu
Koatty Monorepo 迁移任务清单
> **目标**: 将 Koatty 框架迁移到 Monorepo 架构,并配置自动同步
MCPS Master Implementation Protocol (v3.0.0)
| Metadata | Details |