Skip to Content
DocsWhy Event Tracker?

Why Event Tracker?

Modern web applications must continuously analyze user behavior to improve service quality. However, traditional event tracking approaches come with several challenges.

Why You Need Event Tracker

The following example highlights common issues with conventional event tracking.

function Page() { const { user, userId } = useUser(); // Fetch user info and ID. return ( <div> <p>User: {user.name}</p> {/* Passing userId to Counter solely for event tracking */} <Counter userId={userId} /> </div> ); } // Counter receives 'userId' only for tracking purposes. // If nested deeper, this causes severe prop drilling. function Counter({ userId }: { userId: string }) { const [count, setCount] = useState(0); const { track } = useTrackEvent(); // Hypothetical tracking hook const handleIncrement = () => { const newCount = count + 1; setCount(newCount); // Business logic (count increment) mixed with tracking logic. track({ event: "click_increment", params: { type: "count", value: newCount, userId, // userId passed down from parent }, }); }; return ( <div> <p>Count: {count}</p> <button onClick={handleIncrement}>Increment</button> </div> ); }

The Pain of Prop Drilling

To pass tracking-related data down to nested components, you often end up drilling props through multiple layers. This reduces code readability and complicates maintenance.

Tight Coupling of Logic

Mixing business logic with tracking logic increases code complexity and makes independent testing and modification harder.

Increased Boilerplate

Repetitive tracking code reduces developer productivity.

A New Paradigm with Event Tracker

Event Tracker introduces a new paradigm for event tracking. Its declarative approach simplifies the inherent complexity of traditional tracking and makes it accessible to all developers.

Declarative Event Tracking

function Page() { const { user, userId } = useUser(); // Provide tracking context (userId) to child components via Track.Provider. // No more prop drilling required. return ( <Track.Provider initialContext={{ userId }}> <div> <p>User: {user.name}</p> <Counter /> {/* No need to pass userId as a prop */} </div> </Track.Provider> ); } function Counter() { const [count, setCount] = useState(0); const handleIncrement = () => { // handleIncrement is now purely responsible for incrementing count. setCount(count + 1); }; return ( <div> <p>Count: {count}</p> {/* Track.Click wraps the button and tracks the click event with the defined parameters. userId from context is automatically included in the tracking data. */} <Track.Click params={{ value: count + 1, type: "count" }}> <button onClick={handleIncrement}>Increment</button> </Track.Click> </div> ); }

With Event Tracker, declarative event tracking improves code readability and reduces complexity. This helps developers focus on what to track rather than how to track. The handleIncrement function now handles only count logic, while <Track.Click /> manages tracking. This declarative approach shifts the developer’s focus from implementation details to defining the intent of tracking. How tracking is handled is defined outside the React app.

Improved Tracking Cohesion

const [Track, useTracker] = createTracker({ // Callback for DOM events DOMEvents: { onClick: (params, context) => { // Call actual tracking tools (Google Analytics, Amplitude, etc.) logEvent("click_event", { ...params, // { value: ..., type: "count" } userId: context.userId, // Provided by the Provider }); }, // You can also define onMouseOver, onFocus, etc. }, // Callback for Impression events onImpression: (params, context) => { logEvent("impression_event", { ...params, userId: context.userId, pagePath: window.location.pathname, }); }, });

Now, the code defining how to track is separated from business logic. Since it’s located outside the application, you can modify tracking logic without touching business code.

Data Validation

import { z } from "zod"; import { createTracker } from "@offlegacy/event-tracker"; interface Context { /* ... */ } interface Params { /* ... */ } // Define schemas const schemas = { page_view: z.object({ title: z.string(), }), click_button: z.object({ target: z.string(), }), }; // Configure tracker const [Track] = createTracker<Context, Params, typeof schemas>({ schema: { schemas: { page_view, click_button, }, onSchemaError: (error) => { console.error("Schema validation error:", error); }, abortOnError: true, }, }); // Using schemas <Track.PageView schema="page_view" params={{ title: "Home Page" }} />; <Track.Click schema="click_button" params={{ target: "submit-button" }} />;

Event Tracker integrates with Zod  to provide robust schema-based data type validation. This prevents data errors during development and enhances the reliability of tracking data.

Last updated on