Measure Measure
Sign In Start Free Trial
← Blog
react analytics privacy integration

Add Privacy-First Analytics to Your React App

by Jules

React is the most-used JavaScript framework in the world. It also has the worst analytics story. Most developers reach for Google Analytics (a GDPR liability and 45KB of JavaScript), PostHog (powerful but overkill for most apps), or nothing at all.

Measure.events is a different option: a 900-byte tracking script, zero cookies, no consent banners required, and an MCP server so tools like Cursor and Claude can query your analytics directly.

This guide covers setup for Vite React apps, Create React App, and React Router-based SPAs.

Installation

Vite + React

Add the tracking script to your index.html:

<!-- index.html -->
<head>
  <script
    src="https://lets.measure.events/api/script/YOUR_SITE_KEY"
    data-site="YOUR_SITE_KEY"
    defer
  ></script>
</head>

Replace YOUR_SITE_KEY with the key from your Measure.events dashboard.

Create React App

In CRA, add the script to public/index.html:

<!-- public/index.html -->
<head>
  <script
    src="https://lets.measure.events/api/script/%REACT_APP_MEASURE_KEY%"
    data-site="%REACT_APP_MEASURE_KEY%"
    defer
  ></script>
</head>

Set REACT_APP_MEASURE_KEY=your_key in your .env file.

Tracking Page Views in a SPA

React Router doesn’t reload the page between routes, so you need to track navigation changes explicitly. Here’s a custom hook that handles this:

// src/hooks/useMeasure.ts
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

export function useMeasure() {
  const location = useLocation();

  useEffect(() => {
    // Track page view on route change
    if (typeof window !== 'undefined' && (window as any).measure) {
      (window as any).measure('pageview', { url: location.pathname });
    }
  }, [location.pathname]);
}

Then call it in your root component:

// src/App.tsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { useMeasure } from './hooks/useMeasure';

function AppRoutes() {
  useMeasure(); // tracks every route change
  
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/pricing" element={<Pricing />} />
      <Route path="/blog/:slug" element={<BlogPost />} />
    </Routes>
  );
}

export default function App() {
  return (
    <BrowserRouter>
      <AppRoutes />
    </BrowserRouter>
  );
}

Custom Event Tracking

Track button clicks, form submissions, and user interactions:

// src/components/PricingButton.tsx
function PricingButton() {
  const handleClick = () => {
    // Track the event
    if (typeof window !== 'undefined' && (window as any).measure) {
      (window as any).measure('event', {
        name: 'pricing_cta_click',
        props: { plan: 'pro', location: 'hero' }
      });
    }
    // Your existing logic
    router.push('/checkout');
  };

  return (
    <button onClick={handleClick}>
      Get started
    </button>
  );
}

Reusable Event Hook

For cleaner code across your app:

// src/hooks/useTrack.ts
export function useTrack() {
  const track = (name: string, props?: Record<string, unknown>) => {
    if (typeof window !== 'undefined' && (window as any).measure) {
      (window as any).measure('event', { name, props });
    }
  };
  return { track };
}

// Usage
function SignupForm() {
  const { track } = useTrack();

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    track('signup_form_submit', { source: 'homepage' });
    // rest of submit logic
  };
}

TypeScript Types

Add a global type declaration so TypeScript stops complaining:

// src/types/measure.d.ts
interface Window {
  measure: (event: string, data?: {
    url?: string;
    name?: string;
    props?: Record<string, unknown>;
  }) => void;
}

Tracking with React Query or SWR

If you’re fetching data and want to track when users view specific content:

import { useQuery } from '@tanstack/react-query';
import { useTrack } from '../hooks/useTrack';

function ProductPage({ id }: { id: string }) {
  const { track } = useTrack();
  
  const { data } = useQuery({
    queryKey: ['product', id],
    queryFn: () => fetchProduct(id),
    onSuccess: (product) => {
      track('product_view', { productId: id, name: product.name });
    }
  });
}

Next Steps

Once your data is flowing:

  1. Connect the MCP server — add https://lets.measure.events/mcp to your Cursor or Claude setup and ask “which pages are getting the most traffic this week?”
  2. Set up goals — track signups, purchases, and key actions as named events
  3. Share with your team — Measure.events reports are agent-readable, so any AI tool can query them directly

React vs Google Analytics

Measure.eventsGoogle Analytics 4
Script size900 bytes45KB+
Cookie consentNot requiredRequired in EU
Setup time5 minutes30+ minutes
SPA supportManual hook (5 lines)Requires gtag config
AI-queryable✅ Native MCP server
Price$29/moFree (your data is the product)

If you’re shipping a React app in 2026 and care about performance, privacy, or AI-assisted debugging, Measure.events is worth the switch.

Get your site key →

Ready to see accurate analytics?

No cookies. No consent banners. No personal data. $29/mo with a 14-day free trial.

Start free trial →