# Web Analytics

> When to activate: analytics, GA4, event tracking, custom dimensions, conversion tracking, Plausible, attribution, privacy-first

- Skill: `mattakushi432/web-analytics` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mattakushi432/web-analytics`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mattakushi432/web-analytics/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Data & Analytics
- Author: Mattakushi432 (https://skillmd.com/u/mattakushi432)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mattakushi432/web-analytics

---

# Web Analytics Patterns

## GA4 Event Tracking

```js
// gtag helper wrapper
function track(eventName, params = {}) {
  if (typeof gtag === 'undefined') return;
  gtag('event', eventName, params);
}

// Page view (SPA)
track('page_view', {
  page_title: document.title,
  page_location: window.location.href,
});

// E-commerce events
track('view_item', {
  currency: 'USD',
  value: 29.99,
  items: [{ item_id: 'SKU_123', item_name: 'Product', price: 29.99, quantity: 1 }]
});

track('add_to_cart', { currency: 'USD', value: 29.99, items: [...] });

track('purchase', {
  transaction_id: 'T_12345',
  value: 59.98,
  currency: 'USD',
  items: [...]
});
```

## Custom Dimensions & Metrics

```js
// GA4: set user properties
gtag('set', 'user_properties', {
  subscription_tier: 'pro',
  account_age_days: 120,
});

// Custom event with custom params
track('file_download', {
  file_name: 'report.pdf',
  file_extension: 'pdf',
  link_url: '/downloads/report.pdf',
});
```

## React Analytics Hook

```tsx
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

export function usePageTracking() {
  const location = useLocation();
  useEffect(() => {
    track('page_view', {
      page_path: location.pathname + location.search,
      page_title: document.title,
    });
  }, [location]);
}

// Track button clicks declaratively
export function TrackedButton({ eventName, eventParams, ...props }) {
  return (
    <button
      {...props}
      onClick={(e) => {
        track(eventName, eventParams);
        props.onClick?.(e);
      }}
    />
  );
}
```

## Privacy-First Analytics (Plausible)

```html
<!-- No cookies, GDPR compliant -->
<script defer data-domain="example.com"
  src="https://plausible.io/js/script.js"></script>
```

```js
// Custom events with Plausible
window.plausible?.('Signup', { props: { plan: 'pro', source: 'landing' } });
window.plausible?.('Download', { props: { file: 'whitepaper.pdf' } });
```

## Consent Management

```js
// Only load analytics after consent
class ConsentManager {
  #hasConsent = false;
  #queue = [];

  grant() {
    this.#hasConsent = true;
    this.#loadGA4();
    this.#queue.forEach(([name, params]) => this.track(name, params));
    this.#queue = [];
    localStorage.setItem('analytics-consent', 'granted');
  }

  track(name, params) {
    if (this.#hasConsent) {
      gtag('event', name, params);
    } else {
      this.#queue.push([name, params]);
    }
  }

  #loadGA4() {
    const script = document.createElement('script');
    script.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXX';
    script.async = true;
    document.head.append(script);
    window.dataLayer = window.dataLayer || [];
    window.gtag = function() { dataLayer.push(arguments); };
    gtag('js', new Date());
    gtag('config', 'G-XXXXXXXX', { anonymize_ip: true });
  }
}

export const analytics = new ConsentManager();
```

## Core Web Vitals Reporting

```js
import { onCLS, onINP, onLCP, onFCP, onTTFB } from 'web-vitals';

function sendToAnalytics({ name, value, rating, id }) {
  track('web_vitals', {
    metric_name: name,
    metric_value: Math.round(value),
    metric_rating: rating, // 'good' | 'needs-improvement' | 'poor'
    metric_id: id,
  });
}

onCLS(sendToAnalytics);
onINP(sendToAnalytics);
onLCP(sendToAnalytics);
onFCP(sendToAnalytics);
onTTFB(sendToAnalytics);
```

## UTM Parameter Handling

```js
// Persist UTM params through the funnel
const params = new URLSearchParams(location.search);
const utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'];
const utms = Object.fromEntries(utmKeys.filter(k => params.has(k)).map(k => [k, params.get(k)]));

if (Object.keys(utms).length) {
  sessionStorage.setItem('utms', JSON.stringify(utms));
}

// Read back on conversion
const savedUtms = JSON.parse(sessionStorage.getItem('utms') || '{}');
track('purchase', { ...purchaseData, ...savedUtms });
```

