Skip to content

/template recipe-analytics

Commercial License Required

MakerKit requires a commercial license. While Orchestre can help you build with it, you must obtain a valid license from MakerKit for commercial use.

Access: /template recipe-analytics or /t recipe-analytics

Purpose

Implements a privacy-friendly, multi-provider analytics system that allows you to track user behavior, conversions, and custom events while respecting user privacy and GDPR compliance.

How It Actually Works

The recipe:

  1. Analyzes your current tracking setup
  2. Creates an analytics abstraction layer
  3. Implements provider-specific integrations
  4. Adds event tracking throughout your app
  5. Sets up conversion goals and funnels
  6. Ensures privacy compliance with consent management

Use Cases

  • User Behavior Tracking: Page views, sessions, user flows
  • Conversion Tracking: Signups, purchases, upgrades
  • Feature Analytics: Feature adoption, usage patterns
  • Performance Monitoring: Core Web Vitals, load times
  • A/B Testing: Experiment tracking and analysis

Examples

Basic Analytics Setup

bash
/template recipe-analytics plausible
# Implements Plausible Analytics with:
# - Server-side tracking
# - Custom events
# - Goal conversions
# - No cookies required

Multi-Provider Setup

bash
/template recipe-analytics multi posthog plausible
# Creates abstraction layer with:
# - Provider switching capability
# - Unified event API
# - Conditional loading
# - Privacy controls

Enterprise Analytics

bash
/template recipe-analytics enterprise matomo custom-domain
# Sets up self-hosted Matomo with:
# - Custom domain tracking
# - User ID tracking
# - E-commerce analytics
# - Custom dimensions

What Gets Created

1. Analytics Configuration

typescript
// packages/analytics/src/config.ts
export const analyticsConfig = {
  providers: {
    plausible: {
      enabled: process.env.NEXT_PUBLIC_PLAUSIBLE_ENABLED === 'true',
      domain: process.env.NEXT_PUBLIC_PLAUSIBLE_DOMAIN,
      apiHost: process.env.NEXT_PUBLIC_PLAUSIBLE_HOST || 'https://plausible.io'
    },
    posthog: {
      enabled: process.env.NEXT_PUBLIC_POSTHOG_ENABLED === 'true',
      apiKey: process.env.NEXT_PUBLIC_POSTHOG_KEY,
      apiHost: process.env.NEXT_PUBLIC_POSTHOG_HOST
    },
    matomo: {
      enabled: process.env.NEXT_PUBLIC_MATOMO_ENABLED === 'true',
      siteId: process.env.NEXT_PUBLIC_MATOMO_SITE_ID,
      url: process.env.NEXT_PUBLIC_MATOMO_URL
    }
  },
  privacy: {
    requireConsent: true,
    anonymizeIp: true,
    respectDoNotTrack: true
  }
};

2. Analytics Provider

typescript
// packages/analytics/src/provider.tsx
export function AnalyticsProvider({ children }: { children: ReactNode }) {
  const [consent, setConsent] = useState<ConsentState>();
  
  useEffect(() => {
    if (consent?.analytics) {
      initializeAnalytics();
    }
  }, [consent]);
  
  return (
    <AnalyticsContext.Provider value={{ track, identify, page }}>
      {children}
      <ConsentBanner onConsent={setConsent} />
    </AnalyticsContext.Provider>
  );
}

3. Event Tracking System

typescript
// packages/analytics/src/events.ts
export const events = {
  // Authentication events
  auth: {
    signUp: (method: 'email' | 'google' | 'github') => ({
      name: 'sign_up',
      properties: { method }
    }),
    signIn: (method: string) => ({
      name: 'sign_in',
      properties: { method }
    }),
    signOut: () => ({ name: 'sign_out' })
  },
  
  // Billing events
  billing: {
    subscriptionStarted: (plan: string, interval: string) => ({
      name: 'subscription_started',
      properties: { plan, interval }
    }),
    subscriptionUpgraded: (from: string, to: string) => ({
      name: 'subscription_upgraded',
      properties: { from_plan: from, to_plan: to }
    }),
    paymentFailed: (reason: string) => ({
      name: 'payment_failed',
      properties: { reason }
    })
  },
  
  // Feature events
  feature: {
    used: (feature: string, context?: any) => ({
      name: 'feature_used',
      properties: { feature, ...context }
    }),
    discovered: (feature: string) => ({
      name: 'feature_discovered',
      properties: { feature }
    })
  }
};

// Type-safe event tracking
export function trackEvent<T extends keyof typeof events>(
  category: T,
  event: keyof typeof events[T],
  ...args: Parameters<typeof events[T][typeof event]>
) {
  const eventData = events[category][event](...args);
  return track(eventData.name, eventData.properties);
}

4. React Hooks

typescript
// packages/analytics/src/hooks.ts
export function useAnalytics() {
  const analytics = useContext(AnalyticsContext);
  
  return {
    track: analytics.track,
    page: analytics.page,
    identify: analytics.identify,
    trackEvent // Type-safe wrapper
  };
}

export function usePageTracking() {
  const { page } = useAnalytics();
  const pathname = usePathname();
  
  useEffect(() => {
    page(pathname);
  }, [pathname, page]);
}

export function useEventTracking(
  event: string,
  properties?: Record<string, any>
) {
  const { track } = useAnalytics();
  
  return useCallback(() => {
    track(event, properties);
  }, [track, event, properties]);
}

5. Server-Side Tracking

typescript
// packages/analytics/src/server.ts
import { PostHog } from 'posthog-node';
import { headers } from 'next/headers';

const posthog = new PostHog(process.env.POSTHOG_API_KEY!);

export async function trackServerEvent(
  distinctId: string,
  event: string,
  properties?: Record<string, any>
) {
  const headersList = headers();
  const userAgent = headersList.get('user-agent');
  const ip = headersList.get('x-forwarded-for');
  
  await posthog.capture({
    distinctId,
    event,
    properties: {
      ...properties,
      $ip: ip,
      $user_agent: userAgent
    }
  });
}

// Usage in server actions
export async function createProject(data: ProjectData) {
  const project = await db.projects.create(data);
  
  await trackServerEvent(
    data.userId,
    'project_created',
    { project_id: project.id }
  );
  
  return project;
}

6. Conversion Goals

typescript
// packages/analytics/src/goals.ts
export const goals = {
  // Onboarding funnel
  onboarding: {
    started: 'onboarding_started',
    profileCompleted: 'onboarding_profile_completed',
    teamCreated: 'onboarding_team_created',
    firstProjectCreated: 'onboarding_first_project',
    completed: 'onboarding_completed'
  },
  
  // Revenue goals
  revenue: {
    trialStarted: 'trial_started',
    subscriptionCreated: 'subscription_created',
    revenueGenerated: 'revenue_generated'
  }
};

// Track conversion funnel
export function trackFunnelStep(
  funnel: keyof typeof goals,
  step: keyof typeof goals[typeof funnel]
) {
  const goalName = goals[funnel][step];
  track('goal', { name: goalName });
}

Technical Details

Provider Abstraction

typescript
// packages/analytics/src/providers/base.ts
export interface AnalyticsProvider {
  initialize(config: ProviderConfig): Promise<void>;
  track(event: string, properties?: Record<string, any>): void;
  page(path: string, properties?: Record<string, any>): void;
  identify(userId: string, traits?: Record<string, any>): void;
  reset(): void;
}

// Provider implementations
class PlausibleProvider implements AnalyticsProvider {
  async initialize(config) {
    // Load Plausible script
  }
  
  track(event, properties) {
    window.plausible?.(event, { props: properties });
  }
  
  page(path) {
    window.plausible?.('pageview', { u: path });
  }
  
  identify() {
    // Plausible doesn't support user identification
  }
  
  reset() {
    // No-op for Plausible
  }
}

Privacy Compliance

typescript
// packages/analytics/src/consent.tsx
export function ConsentBanner() {
  const [show, setShow] = useState(false);
  
  useEffect(() => {
    const consent = getStoredConsent();
    if (!consent) {
      setShow(true);
    }
  }, []);
  
  const handleConsent = (choices: ConsentChoices) => {
    storeConsent(choices);
    
    if (choices.analytics) {
      initializeAnalytics();
    }
    
    if (choices.marketing) {
      initializeMarketing();
    }
    
    setShow(false);
  };
  
  if (!show) return null;
  
  return (
    <div className="consent-banner">
      <h3>Privacy Settings</h3>
      <p>We use cookies and similar technologies to:</p>
      <ConsentOptions onAccept={handleConsent} />
    </div>
  );
}

Custom Events

typescript
// Track custom business metrics
export function trackBusinessMetric(
  metric: string,
  value: number,
  metadata?: Record<string, any>
) {
  track('business_metric', {
    metric,
    value,
    ...metadata
  });
  
  // Send to monitoring service
  if (process.env.NODE_ENV === 'production') {
    sendToDatadog(metric, value, metadata);
  }
}

Memory Evolution

The recipe updates CLAUDE.md:

markdown
## Analytics System

The application uses a multi-provider analytics system:

### Providers
- Plausible: Primary analytics (privacy-friendly)
- PostHog: Product analytics and feature flags
- Custom: Business metrics to Datadog

### Key Events
- Authentication: sign_up, sign_in, sign_out
- Billing: subscription_started, payment_failed
- Features: feature_used, feature_discovered
- Business: mrr_updated, churn_prevented

### Privacy
- GDPR compliant with consent management
- No PII in analytics by default
- IP anonymization enabled
- Respects Do Not Track

### Implementation
- Provider abstraction in packages/analytics
- Server and client-side tracking
- Type-safe event system
- Conversion goal tracking

Best Practices

  1. Privacy First: Always prioritize user privacy
  2. Meaningful Events: Track actions, not just page views
  3. Consistent Naming: Use standardized event names
  4. Avoid PII: Never send personal information
  5. Test Tracking: Verify events in development

Integration Points

  • Authentication: Track signup/signin methods
  • Billing: Monitor subscription lifecycle
  • Features: Measure adoption and usage
  • Performance: Track Core Web Vitals
  • Errors: Capture and analyze failures

Common Patterns

Feature Adoption Tracking

typescript
export function FeatureWrapper({ feature, children }) {
  const { trackEvent } = useAnalytics();
  const [discovered, setDiscovered] = useState(false);
  
  useEffect(() => {
    if (!discovered) {
      trackEvent('feature', 'discovered', feature);
      setDiscovered(true);
    }
  }, []);
  
  const handleUse = () => {
    trackEvent('feature', 'used', feature);
  };
  
  return (
    <div onMouseDown={handleUse}>
      {children}
    </div>
  );
}

Conversion Funnel

typescript
// Track progression through signup
export function SignupFlow() {
  const { trackFunnelStep } = useAnalytics();
  
  useEffect(() => {
    trackFunnelStep('onboarding', 'started');
  }, []);
  
  const handleProfileComplete = () => {
    trackFunnelStep('onboarding', 'profileCompleted');
  };
  
  // Continue for each step...
}

Next Steps

  • Choose your analytics providers
  • Configure privacy settings
  • Define conversion goals
  • Set up custom events
  • Create analytics dashboards

Built with ❤️ for the AI Coding community, by Praney Behl