This library enables tracking of UI events when a user interacts with a React or React Native application.
- Progressive - Uses latest JavaScript features and design patterns for a React codebase.
- Extensible - A modular architecture and usage of Dependency.Inversion patterns gives you flexibility and allows you to easily extend features.
- TypeScript support.
// import your component
import Button from 'Button';
import withTracking from 'react-user-analytics/index';
const ButtonWithTracking = withTracking(Button);
<ButtonWithTracking
label="click me"
trackers={[
{
action: 'onClick',
track: handleLogEvent,
},
]}
/>
- Form Events
onChange
- Mouse Events
onClick
onHover
This project uses Higher-Order Components (HOCs) to wrap existing components with tracking logic β without modifying the components themselves.
By using HOCs:
-
β No rewrites required β Existing components stay untouched.
-
π§© Plug-and-play tracking β Tracking logic is attached where needed, with minimal friction.
-
π§ͺ A creative HOC experiment β This project explores how far we can take HOCs to inject behavior cleanly and declaratively.
-
π§ Customizable data structure β Gain precise control over how user interaction events are structured and sent, instead of being limited by black-box analytics tools.
This approach makes it easy to scale analytics across an application without tangling up UI code β just wrap and go.
Eventually, the library will have exhaustive coverage and support for many more events, such as:
-
Wheel Events
-
Touch Events
-
Keyboard Events
-
Mouse Events
-
and more.
-
User Interaction journey mapping
-
Session Recording
-
First, import the library in your project.
-
In-order to add user-tracking ability to your component or element, import the
withTracking
function (Higher-order component) and wrap the component. Here's an example using a simpleButton
component:
export const ButtonWithTracking = withTracking(Button)
ButtonWithTracking
will have all the features and properties of the Button
component, but with interaction-tracking superpowers!
- Finally, use
ButtonWithTracking
inside your app anywhere where you'd like to track user-events occurring on this component, such asonClick
oronHover
.
import Button from 'components/Button';
import { withTracking } from 'react-user-analytics/index';
const ButtonWithTracking = withTracking(Button);
function Home() {
function handleClick(e: React.MouseEvent<HTMLElement, MouseEvent>) {
// app logic goes here
}
function logEvent(
event: React.MouseEvent<HTMLElement, MouseEvent>,
interactionResource: UserInteractionResource
) {
/*
do whatever you want with the resource,
like save it to IndexedDB, compress it, save it via API, etc
*/
console.log(interactionResource);
}
return (
<ButtonWithTracking
type="text"
onClick={handleClick}
trackers={[
// track onClick event
{
action: "onClick", // event to track
track: logEvent, // callback function that runs whenever the event occurs
}
]}
>
)
}
export default Home;
You can add multiple tracker objects within the trackers
array if you need to track more than one event occurring within the component.
Use-case:
Say you have 2 react components - a ButtonWithTracking
configured to track onClick
events, and a InputWithTracking
component configured to capture onChange
events. These components are being used in 2 different pages or templates in your application - a login form, and a newsletter subscription form.
In this scenario, it is useful to capture a global 'context' within which the events occur - such as the page or the container component details, and the app version. This information is useful to plot out the user's journey, which will give you a more contextual understanding of how the user navigates through your app.
-
Using the
<DataContext.Provider>
, you can provide the global 'context' to your tracking components without having to pass them explicitly via props. Here's how:- Create a
DataContext
object.
- Create a
const dataContext = {
context: "Login Form",
app: {
version: "1",
},
} as UserInteraction.DataContext;
- Next, wrap your template or container component within
DataContext.Provider
and provide it thedataContext
value:
import { withTracking, DataContext, UserInteractionResource } from 'react-user-analytics/index';
import Button from 'components/Button';
const ButtonWithTracking = withTracking(Button);
function LoginForm() {
function logEvent(
event: React.MouseEvent<HTMLElement, MouseEvent>,
interactionResource: UserInteractionResource
) {
console.log(interactionResource.app.version) // Will print "1"
console.log(interactionResource.source.context); // Will print "Login Form"
}
return (
<DataContext.Provider value={dataContext}> // Pass the dataContext value
<ButtonWithTracking
type="text"
onClick={handleClick}
trackers={[
// track onClick event
{
action: "onClick",
track: logEvent,
data: { // pass optional custom data
color: "blue",
}
}
]}
>
</DataContext.Provider>
)
}
export default LoginForm;
This way, your tracking components nested anywhere within the provider will receive the dataContext
object and will return it as part of the UserInteractionResource
object.
If you don't want to provide data using DataContext.Provider
or want to override it with a different value, you can pass them explicitly via props:
function LoginForm() {
function logEvent(
event: React.MouseEvent<HTMLElement, MouseEvent>,
interactionResource: UserInteractionResource
) {
console.log(interactionResource.app.version) // Will print "0"
console.log(interactionResource.source.context); // Will print "Login Form"
}
return (
<ButtonWithTracking
type="text"
onClick={handleClick}
trackers={[
// track onClick event
{
action: "onClick",
track: logEvent,
data: { // pass optional custom data
color: "blue",
}
}
]}
dataContext={{ // Pass dataContext explicitly
app: {
version: "0",
},
context: "Login Form"
}}
>
)
}
export default LoginForm;
In-case you have both in your application, the data context passed via props will override the values from <DataContext.Provider>
-
The tracking-enabled component will accept all props required for the original component, along with the following:
Props Required Description Type trackers
Yes Each tracker object expects an action
andtrack
properties. Check the section below for the complete list of propertiesUserInteraction.Tracker[]
origin
Optional To provide some contextual information for the event origin string
dataContext
Optional an object property to provide context of the taken event UserInteraction.DataContext
-
Property Required Description Type action
Yes Type of event that needs to be tracked (React Synthetic events). Can take values such as onClick
,onChange
string
track
Yes Callback that runs when above event occurs (e, interactionResource: UserInteractionResource) => void
data
Optional Can be used to provide some custom data. Accessible within UserInteractionResource.data
Object<any>