Free15 minutesintermediate

React Integration

Integrate Zenovay with React applications - tracking, hooks, and component-based analytics.

reactjavascriptintegrationspatracking
Last updated: January 15, 2025

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

Next Steps

Was this article helpful?