analytics/docs/react-integration.md
Lilith dc5329e885 chore(docs): 📝 Update documentation files in /docs directory (README, guides, or API references)
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-01-29 08:20:58 -08:00

8.7 KiB

React Integration Guide

Integrate analytics into React applications with hooks and context.

Installation

npm install @analytics/client

Setup

1. Create Analytics Provider

// analytics-provider.tsx
import { createContext, useContext, useMemo, type ReactNode } from 'react';
import { AnalyticsClient, type AnalyticsConfig } from '@analytics/client';

interface AnalyticsContextValue {
  client: AnalyticsClient;
  trackEvent: (type: string, action: string, metadata?: Record<string, unknown>) => void;
  identify: (userId: string, traits?: Record<string, unknown>) => void;
}

const AnalyticsContext = createContext<AnalyticsContextValue | null>(null);

interface AnalyticsProviderProps {
  children: ReactNode;
  config: AnalyticsConfig;
}

export function AnalyticsProvider({ children, config }: AnalyticsProviderProps) {
  const value = useMemo(() => {
    const client = new AnalyticsClient(config);

    return {
      client,
      trackEvent: (type, action, metadata) => {
        client.trackEngagement({ type, action, metadata });
      },
      identify: (userId, traits) => {
        client.identify(userId, traits);
      },
    };
  }, [config]);

  return (
    <AnalyticsContext.Provider value={value}>
      {children}
    </AnalyticsContext.Provider>
  );
}

export function useAnalytics() {
  const context = useContext(AnalyticsContext);
  if (!context) {
    throw new Error('useAnalytics must be used within AnalyticsProvider');
  }
  return context;
}

2. Wrap Your App

// App.tsx
import { AnalyticsProvider } from './analytics-provider';

function App() {
  return (
    <AnalyticsProvider
      config={{
        apiBaseUrl: import.meta.env.VITE_ANALYTICS_URL,
        appName: 'my-react-app',
        enabled: import.meta.env.PROD,
      }}
    >
      <Router />
    </AnalyticsProvider>
  );
}

Hooks

usePageTracking

Track page views on route changes.

// hooks/use-page-tracking.ts
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { useAnalytics } from '../analytics-provider';

export function usePageTracking() {
  const location = useLocation();
  const { trackEvent } = useAnalytics();

  useEffect(() => {
    trackEvent('navigation', 'page_view', {
      path: location.pathname,
      search: location.search,
    });
  }, [location.pathname, location.search, trackEvent]);
}

// Usage: Call once in your root component
function AppContent() {
  usePageTracking();
  return <Outlet />;
}

useClickTracking

Track element clicks.

// hooks/use-click-tracking.ts
import { useCallback } from 'react';
import { useAnalytics } from '../analytics-provider';

export function useClickTracking() {
  const { trackEvent } = useAnalytics();

  return useCallback((
    elementName: string,
    metadata?: Record<string, unknown>
  ) => {
    trackEvent('click', elementName, metadata);
  }, [trackEvent]);
}

// Usage
function CTAButton() {
  const trackClick = useClickTracking();

  return (
    <button onClick={() => trackClick('signup_cta', { location: 'hero' })}>
      Sign Up
    </button>
  );
}

useFormTracking

Track form interactions.

// hooks/use-form-tracking.ts
import { useCallback } from 'react';
import { useAnalytics } from '../analytics-provider';

export function useFormTracking(formName: string) {
  const { trackEvent } = useAnalytics();

  const trackStart = useCallback(() => {
    trackEvent('form', 'start', { formName });
  }, [trackEvent, formName]);

  const trackField = useCallback((fieldName: string) => {
    trackEvent('form', 'field_focus', { formName, fieldName });
  }, [trackEvent, formName]);

  const trackSubmit = useCallback((success: boolean, error?: string) => {
    trackEvent('form', success ? 'submit_success' : 'submit_error', {
      formName,
      success,
      error,
    });
  }, [trackEvent, formName]);

  const trackAbandonment = useCallback((lastField?: string) => {
    trackEvent('form', 'abandonment', { formName, lastField });
  }, [trackEvent, formName]);

  return { trackStart, trackField, trackSubmit, trackAbandonment };
}

useScrollTracking

Track scroll depth.

// hooks/use-scroll-tracking.ts
import { useEffect, useRef } from 'react';
import { useAnalytics } from '../analytics-provider';

export function useScrollTracking() {
  const { trackEvent } = useAnalytics();
  const trackedDepths = useRef(new Set<number>());

  useEffect(() => {
    const thresholds = [25, 50, 75, 90, 100];

    const handleScroll = () => {
      const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;
      const scrollPercent = Math.round((window.scrollY / scrollHeight) * 100);

      for (const threshold of thresholds) {
        if (scrollPercent >= threshold && !trackedDepths.current.has(threshold)) {
          trackedDepths.current.add(threshold);
          trackEvent('scroll', 'depth', { percent: threshold });
        }
      }
    };

    window.addEventListener('scroll', handleScroll, { passive: true });
    return () => window.removeEventListener('scroll', handleScroll);
  }, [trackEvent]);
}

Component Patterns

import { Link, type LinkProps } from 'react-router-dom';
import { useClickTracking } from '../hooks/use-click-tracking';

interface TrackedLinkProps extends LinkProps {
  trackingName: string;
  trackingMetadata?: Record<string, unknown>;
}

export function TrackedLink({
  trackingName,
  trackingMetadata,
  onClick,
  ...props
}: TrackedLinkProps) {
  const trackClick = useClickTracking();

  const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
    trackClick(trackingName, trackingMetadata);
    onClick?.(e);
  };

  return <Link {...props} onClick={handleClick} />;
}

Tracked Button

interface TrackedButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
  trackingName: string;
  trackingMetadata?: Record<string, unknown>;
}

export function TrackedButton({
  trackingName,
  trackingMetadata,
  onClick,
  ...props
}: TrackedButtonProps) {
  const trackClick = useClickTracking();

  const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
    trackClick(trackingName, trackingMetadata);
    onClick?.(e);
  };

  return <button {...props} onClick={handleClick} />;
}

Impression Tracking

Track when elements become visible.

import { useEffect, useRef } from 'react';
import { useAnalytics } from '../analytics-provider';

interface UseImpressionTrackingOptions {
  elementName: string;
  metadata?: Record<string, unknown>;
  threshold?: number;
  trackOnce?: boolean;
}

export function useImpressionTracking({
  elementName,
  metadata,
  threshold = 0.5,
  trackOnce = true,
}: UseImpressionTrackingOptions) {
  const { trackEvent } = useAnalytics();
  const ref = useRef<HTMLElement>(null);
  const hasTracked = useRef(false);

  useEffect(() => {
    const element = ref.current;
    if (!element) return;

    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          if (trackOnce && hasTracked.current) return;
          hasTracked.current = true;
          trackEvent('impression', elementName, metadata);
        }
      },
      { threshold }
    );

    observer.observe(element);
    return () => observer.disconnect();
  }, [elementName, metadata, threshold, trackOnce, trackEvent]);

  return ref;
}

// Usage
function ProductCard({ product }) {
  const ref = useImpressionTracking({
    elementName: 'product_card',
    metadata: { productId: product.id },
  });

  return <div ref={ref}>...</div>;
}

Best Practices

1. Track at the Right Level

// ❌ Don't track everything
onClick={() => {
  trackClick('button');
  trackClick('cta');
  trackClick('hero_cta');
}}

// ✅ Track meaningful, specific events
onClick={() => trackClick('hero_signup_cta')}

2. Use Consistent Naming

// ❌ Inconsistent naming
trackEvent('click', 'SignUp');
trackEvent('user_action', 'sign-up');
trackEvent('button', 'signup_button');

// ✅ Consistent naming convention
trackEvent('click', 'signup_cta');
trackEvent('click', 'login_cta');
trackEvent('click', 'pricing_link');

3. Include Context

// ❌ Missing context
trackEvent('click', 'buy_button');

// ✅ Rich context
trackEvent('click', 'buy_button', {
  productId: product.id,
  price: product.price,
  location: 'product_page',
  variant: selectedVariant,
});

4. Handle Loading States

function AnalyticsWrapper({ children }) {
  const [ready, setReady] = useState(false);

  useEffect(() => {
    // Initialize analytics
    setReady(true);
  }, []);

  if (!ready) {
    // Render children without tracking during init
    return <>{children}</>;
  }

  return (
    <AnalyticsProvider config={...}>
      {children}
    </AnalyticsProvider>
  );
}