Integrate Zenovay analytics into your React application with automatic page tracking, custom events, and a custom hook for the global window.zenovay function.
Installation
Add the Zenovay tracking script to your index.html:
<script
defer
data-tracking-code="YOUR_TRACKING_CODE"
src="https://api.zenovay.com/z.js"
></script>
No npm package is needed. The script automatically tracks page views and provides the global window.zenovay function for custom tracking.
Basic Setup
Create a useZenovay Hook
Create a custom hook to wrap the global window.zenovay function:
// hooks/useZenovay.js
export function useZenovay() {
const trackEvent = (name, properties) => {
if (window.zenovay) {
window.zenovay('track', name, properties);
}
};
const trackGoal = (name, properties) => {
if (window.zenovay) {
window.zenovay('goal', name, properties);
}
};
const trackPageview = () => {
if (window.zenovay) {
window.zenovay('page');
}
};
const trackRevenue = (amount, currency, meta) => {
if (window.zenovay) {
window.zenovay('revenue', amount, currency, meta);
}
};
const identify = (userId, properties) => {
if (window.zenovay) {
window.zenovay('identify', userId, properties);
}
};
return { trackEvent, trackGoal, trackPageview, trackRevenue, identify };
}
Automatic Page Tracking
With React Router
The Zenovay script automatically tracks the initial page load. For SPA route changes with React Router, track page views on route change:
import { BrowserRouter, useLocation } from 'react-router-dom';
import { useEffect } from 'react';
function PageTracker() {
const location = useLocation();
useEffect(() => {
if (window.zenovay) {
window.zenovay('page');
}
}, [location.pathname]);
return null;
}
function App() {
return (
<BrowserRouter>
<PageTracker />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/pricing" element={<Pricing />} />
</Routes>
</BrowserRouter>
);
}
Page views tracked automatically on route changes.
Custom Events
useZenovay Hook
import { useZenovay } from './hooks/useZenovay';
function SignupButton() {
const { trackEvent } = useZenovay();
const handleSignup = () => {
// Track the signup click
trackEvent('signup_click', {
plan: 'pro',
source: 'pricing_page'
});
// Continue with signup logic
performSignup();
};
return (
<button onClick={handleSignup}>
Start Free Trial
</button>
);
}
Track Event Function
const { trackEvent } = useZenovay();
// Simple event
trackEvent('button_click');
// With properties
trackEvent('purchase', {
product_id: 'SKU-001',
value: 99.99,
currency: 'USD'
});
// With value
trackEvent('download', { file: 'guide.pdf' });
Goal Tracking
Track Goal Completion
import { useZenovay } from './hooks/useZenovay';
import { useEffect } from 'react';
function CheckoutSuccess({ order }) {
const { trackGoal } = useZenovay();
useEffect(() => {
trackGoal('purchase', {
value: order.total,
order_id: order.id,
products: order.items
});
}, [order]);
return <div>Thank you for your order!</div>;
}
User Identification
Identify Users
import { useZenovay } from './hooks/useZenovay';
import { useAuth } from './auth';
import { useEffect } from 'react';
function UserIdentifier() {
const { user, isAuthenticated } = useAuth();
const { identify } = useZenovay();
useEffect(() => {
if (isAuthenticated && user) {
identify(user.id, {
email: user.email,
name: user.name,
plan: user.subscription
});
}
}, [user, isAuthenticated]);
return null;
}
In Auth Flow
async function handleLogin(credentials) {
const user = await login(credentials);
// Identify after login
if (window.zenovay) {
window.zenovay('identify', user.id, {
email: user.email
});
}
navigate('/dashboard');
}
Revenue Tracking
Track Revenue
import { useZenovay } from './hooks/useZenovay';
import { useEffect } from 'react';
function OrderConfirmation({ order }) {
const { trackRevenue } = useZenovay();
useEffect(() => {
trackRevenue(order.total, 'USD', {
order_id: order.id,
items: order.items.map(item => ({
id: item.sku,
name: item.name,
price: item.price,
quantity: item.quantity
}))
});
}, [order]);
return (
<div>
<h1>Order Confirmed!</h1>
<p>Order #{order.id}</p>
</div>
);
}
Event Tracking Components
Trackable Button
import { useZenovay } from './hooks/useZenovay';
function TrackableButton({ event, eventData, children, ...props }) {
const { trackEvent } = useZenovay();
const handleClick = (e) => {
trackEvent(event, eventData);
props.onClick?.(e);
};
return (
<button {...props} onClick={handleClick}>
{children}
</button>
);
}
// Usage
<TrackableButton
event="cta_click"
eventData={{ location: 'hero' }}
onClick={handleSignup}
>
Get Started
</TrackableButton>
Track on Mount
import { useZenovay } from './hooks/useZenovay';
import { useEffect } from 'react';
function TrackOnMount({ event, eventData }) {
const { trackEvent } = useZenovay();
useEffect(() => {
trackEvent(event, eventData);
}, []);
return null;
}
// Usage
<TrackOnMount
event="pricing_viewed"
eventData={{ referrer: document.referrer }}
/>
TypeScript Support
Types
// types/zenovay.d.ts
interface ZenovayFunction {
(command: 'track', name: string, properties?: Record<string, unknown>): void;
(command: 'identify', userId: string, traits?: Record<string, unknown>): void;
(command: 'goal', name: string, properties?: Record<string, unknown>): void;
(command: 'page'): void;
(command: 'revenue', amount: number, currency: string, meta?: Record<string, unknown>): void;
}
declare global {
interface Window {
zenovay: ZenovayFunction;
}
}
export {};
Typed Hook
// hooks/useZenovay.ts
export function useZenovay() {
const trackEvent = (name: string, properties?: Record<string, unknown>) => {
if (window.zenovay) {
window.zenovay('track', name, properties);
}
};
const trackGoal = (name: string, properties?: Record<string, unknown>) => {
if (window.zenovay) {
window.zenovay('goal', name, properties);
}
};
const identify = (userId: string, properties?: Record<string, unknown>) => {
if (window.zenovay) {
window.zenovay('identify', userId, properties);
}
};
return { trackEvent, trackGoal, identify };
}
Server-Side Rendering
Prevent SSR Issues
The window.zenovay function is only available in the browser. Always check before using:
import { useEffect, useState } from 'react';
function ZenovayWrapper({ children }) {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
// Only track after component mounts (client-side)
if (!mounted) {
return children;
}
return children;
}
Testing
Mock the Hook
// test-utils.jsx
import { vi } from 'vitest';
export const mockZenovay = {
trackEvent: vi.fn(),
trackGoal: vi.fn(),
identify: vi.fn(),
trackPageview: vi.fn(),
trackRevenue: vi.fn()
};
vi.mock('./hooks/useZenovay', () => ({
useZenovay: () => mockZenovay
}));
Test Example
import { render, fireEvent, screen } from '@testing-library/react';
import { mockZenovay } from './test-utils';
import SignupButton from './SignupButton';
test('tracks signup click', () => {
render(<SignupButton />);
fireEvent.click(screen.getByText('Sign Up'));
expect(mockZenovay.trackEvent).toHaveBeenCalledWith('signup_click', expect.any(Object));
});
Common Patterns
E-commerce Tracking
import { useZenovay } from './hooks/useZenovay';
import { useEffect } from 'react';
function ProductPage({ product }) {
const { trackEvent } = useZenovay();
useEffect(() => {
trackEvent('product_viewed', {
product_id: product.id,
product_name: product.name,
price: product.price,
category: product.category
});
}, [product.id]);
const handleAddToCart = () => {
trackEvent('add_to_cart', {
product_id: product.id,
price: product.price,
quantity: 1
});
};
return (
<div>
<h1>{product.name}</h1>
<button onClick={handleAddToCart}>Add to Cart</button>
</div>
);
}
Form Tracking
import { useZenovay } from './hooks/useZenovay';
function ContactForm() {
const { trackEvent, trackGoal } = useZenovay();
const handleSubmit = async (data) => {
trackEvent('form_submitted', {
form_name: 'contact',
has_phone: !!data.phone
});
await submitForm(data);
trackGoal('contact_form');
};
return (
<form onSubmit={handleSubmit}>
{/* form fields */}
</form>
);
}
Troubleshooting
Events Not Tracking
Check:
- Script tag is in your index.html
- Tracking code is correct
- Not blocked by ad blocker
- Console for errors
Page Views Missing
Check:
- PageTracker component is mounted
- React Router is set up correctly
- Routes render inside the router
Double Tracking
Avoid:
- Multiple script tags
- Manual + automatic tracking
- StrictMode causing double effects