Event tracking is a complex task that requires a lot of boilerplate code and complexity.
Take a look at this example.
// Traditional event tracking
const Page = () => {
const { user, userId } = useUser();
return (
<div>
<p>User: {user.name}</p>
<Counter userId={userId} />
</div>
);
};
const Counter = ({ userId }: { userId: string }) => {
// Receives 'userId' as a prop just for event tracking purposes.
const [count, setCount] = useState(0);
const { track } = useTrackEvent();
const handleIncrement = () => {
setCount(count + 1);
track({
event: "click",
params: {
type: "count",
value: count + 1,
userId,
},
});
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleIncrement}>Increment</button>
</div>
);
};The two main inconveniences caused by event tracking in the code are:
- Prop Drilling: The
userIdis passed as a prop to the<Counter />component from the<Page />component. If the<Counter />component is nested deeper within the component tree, prop drilling can become more severe, reducing the readability and maintainability of the code. - Coupling of Event Tracking Logic and Business Logic: The
handleIncrementfunction contains both the logic for incrementing the count and tracking the event. Separating the event tracking logic can make the code cleaner and easier to maintain.
event-tracker introduces a new paradigm for event tracking.
The declarative nature of this paradigm simplifies
the complexity traditionally associated with event tracking, making it accessible to developers of all skill levels.
const Page = () => {
const { user, userId } = useUser();
return (
<Track.Provider initialContext={{ userId }}>
<div>
<p>User: {user.name}</p>
<Counter />
</div>
</Track.Provider>
);
};
const Counter = () => {
const [count, setCount] = useState(0);
const handleIncrement = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<Track.Click params={{ value: count + 1, type: "count" }}>
<button onClick={handleIncrement}>Increment</button>
</Track.Click>
</div>
);
};Using event-tracker allows for declarative event tracking, which improves code readability and reduces complexity. This helps developers understand and use event tracking more easily.
Your handleIncrement function is now only responsible for incrementing the count, and the event tracking is handled by the <Track.Click /> component.
This declarative approach makes the developer focus on 'what to track' and not 'how to track'.
How to track should be defined outside of the React app.
const [Track] = createTracker({
DOMEvents: {
onClick: (params, context) => {
log({
event: "click",
params: {
...params,
userId: context.userId,
},
});
},
},
onImpression: (params, context) => {
log({
event: "impression",
params: {
...params,
userId: context.userId,
},
});
},
});Your instructions for 'how to track' is now separated from your business logic.
It's located outside your application, so you can change your event tracking provider without having to change your application code.
- 🎯 Type-safe APIs: Declarative event tracking with complete type safety
- ⚡️ Optimized Performance: Enhanced performance through event batching
- 🔄 Guaranteed Order: Guaranteed execution order for async operations
- 🔌 Analytics Agnostic: Works with any analytics provider of your choice
- 🧩 Clean Separation: Keeps tracking logic separate from business logic
- 📦 Lightweight: Minimal bundle size impact on your application
event-tracker is built around a few core concepts:
- Tracker Creation: Use
createTrackerto create a tracker instance with your desired event tracking instructions. - Provider: Wrap your app with
Track.Providerto provide context. - Event Components: Use components like
Track.ClickorTrack.Impressionto track events. - Hooks: Use the
useTrackerhook to access tracking functionality in your components imperatively.
Check out the other sections for detailed documentation on each feature:
- createTracker - Detailed API documentation on
createTracker - components - Available tracking components
- hook - Track using the hook
- Batching - Optimizing performance with event batching