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:
- Connect the MCP server — add
https://lets.measure.events/mcpto your Cursor or Claude setup and ask “which pages are getting the most traffic this week?” - Set up goals — track signups, purchases, and key actions as named events
- Share with your team — Measure.events reports are agent-readable, so any AI tool can query them directly
React vs Google Analytics
| Measure.events | Google Analytics 4 | |
|---|---|---|
| Script size | 900 bytes | 45KB+ |
| Cookie consent | Not required | Required in EU |
| Setup time | 5 minutes | 30+ minutes |
| SPA support | Manual hook (5 lines) | Requires gtag config |
| AI-queryable | ✅ Native MCP server | ❌ |
| Price | $29/mo | Free (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.
Ready to see accurate analytics?
No cookies. No consent banners. No personal data. $29/mo with a 14-day free trial.
Start free trial →