Declarative Tracking for React Apps
react-tracking is a declarative and imperative tracking library specifically designed for React applications. It provides an analytics platform-agnostic solution, empowering developers to manage and dispatch user interaction data effectively. The library's core philosophy is to compartmentalize tracking concerns directly within individual components, thereby preventing data leakage and ensuring a clean separation of concerns across the entire application. It offers a dual API approach, supporting both the traditional Higher-Order Component (HoC) or decorator pattern and a modern React Hooks API, which was fully introduced in version 8.1.0 for functional components. The current stable version, 9.3.2, has recently addressed compatibility issues with React Native environments and re-enabled support for Node.js 16.9+. Developed by The New York Times, react-tracking is actively maintained, with a consistent cadence of patch and minor releases addressing bug fixes and introducing new features like `mergeOptions` (v9.3.0) and `deepmerge` re-export (v9.2.0). Its key differentiators include its highly declarative nature, flexibility to integrate seamlessly with virtually any analytics backend, and comprehensive support for both class-based and modern functional React components, making it a versatile choice for instrumenting React UIs.
Common errors
-
TypeError: Cannot destructure property `trackEvent` of 'null' or 'undefined'
cause Decorating an async method in `react-tracking` versions prior to 7.3.0 could lead to `trackEvent` being called with `null` or an unexpected value when an error occurred.fixUpgrade `react-tracking` to version `7.3.0` or newer. If unable to upgrade, ensure your decorated async methods handle potential `null` or `undefined` values when calling `trackEvent`. -
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
cause `useTracking` or other React Hooks are being called outside of a functional React component or a custom Hook.fixEnsure `useTracking` is only invoked directly within the body of a React functional component or another custom React Hook function. Do not call Hooks from regular JavaScript functions, class components, or event handlers directly. -
Module not found: Error: Can't resolve 'react-tracking'
cause The `react-tracking` package is not installed, or there is a typo in the import path.fixRun `npm install --save react-tracking` or `yarn add react-tracking`. Double-check the import statement for any typos, ensuring it is `import { useTracking } from 'react-tracking';` or `import track from 'react-tracking';`.
Warnings
- breaking Version 9.0.0 removed `core-js` polyfills from the bundle to reduce build size. Applications must now provide their own polyfills if targeting older browser environments.
- gotcha When using the HoC/decorator pattern, accessing the underlying component's ref requires explicitly setting `forwardRef: true` in the options. Failing to do so will result in the ref pointing to the HoC wrapper instead of the intended component.
- gotcha Prior to v7.3.0, decorating asynchronous methods (or methods returning Promises) could lead to incorrect return values or `TypeError` exceptions if `trackEvent` was called with `null`.
- gotcha When `dispatchOnMount` is provided as a function, it is called in a `useEffect` on the component's initial render with all tracking context data. Be mindful of potential side effects or performance implications if the function performs heavy operations.
- gotcha Version 9.3.0 of `react-tracking` temporarily dropped support for Node.js 16.9+ due to an oversight, only to re-enable it in 9.3.1. Ensure you are on 9.3.1 or later if using Node.js 16.9+.
Install
-
npm install react-tracking -
yarn add react-tracking -
pnpm add react-tracking
Imports
- track
import { track } from 'react-tracking';import track from 'react-tracking';
- useTracking
const { useTracking } = require('react-tracking');import { useTracking } from 'react-tracking'; - Track
import { track } from 'react-tracking';import { Track } from 'react-tracking'; - deepmerge
import deepmerge from 'deepmerge';
import { deepmerge } from 'react-tracking';
Quickstart
import React, { useEffect } from 'react';
import { useTracking, Track } from 'react-tracking';
interface MyComponentProps {
userId: string;
pageName: string;
}
const ProductPage: React.FC<MyComponentProps> = ({ userId, pageName }) => {
// Initialize tracking for this component, passing contextual data
const { Track, trackEvent, getTrackingData } = useTracking(
{
page: pageName,
user: userId,
environment: process.env.NODE_ENV ?? 'development',
},
{
// Optional: Dispatch tracking data when component mounts
dispatchOnMount: true,
// Optional: Custom merge options for tracking objects (since v9.3.0)
mergeOptions: {
isMergeableObject: obj => !(obj instanceof Date || obj instanceof RegExp),
},
}
);
useEffect(() => {
// Example: Log current tracking data when component mounts
console.log('Current tracking context:', getTrackingData());
}, []);
const handleProductClick = (productId: string) => {
// Imperatively dispatch an event
trackEvent({
action: 'product_click',
productId: productId,
timestamp: new Date().toISOString(),
});
};
return (
<Track> {/* Use <Track> to pass context to children */}
<div>
<h1>{pageName} for User: {userId}</h1>
<p>This is a product page example.</p>
<button onClick={() => handleProductClick('product-123')}>
Click to Track Product 123
</button>
<button onClick={() => trackEvent({ action: 'promo_view', promoId: 'summer_sale' })}>
Track Promo View
</button>
</div>
</Track>
);
};
// Example of usage:
// <ProductPage userId="user-abc" pageName="Electronics-Category" />
export default ProductPage;