Analytics examples, using Amplitude vendor
Amplitude is great for getting started but if you need more than what the free plan offers, then you better make sure you can afford it.
It's much more comfortable from a developer standpoint that everything we've worked with by the past.
We only use Amplitude from the client, mostly because Amplitude didn't provide a nodejs compatible library until very recently.
Also, we prefer to perform all reporting on the client side, as it avoids issues with multiple events sent by mistake.
The app is configured in a way that all usual web-related analytics options are handled out the box.
It also comes with a shared configuration for all pages (see below) and user-session tracking.
Regarding GDPR concerns, the IP address is not processed/stored by any vendor (analytics data are anonymous). Several cookies are created on the device if the user hasn't opted out of tracking (through the Cookie Consent popup).
Shared analytics configuration
The below code is the shared configuration between all pages.
It initializes the whole thing, and automatically track app-wide data so that all event will contain those properties automatically.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<AmplitudeProvider
amplitudeInstance={amplitudeInstance}
apiKey={process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY}
userId={userId}
>
<Amplitude
eventProperties={{
app: {
name: process.env.NEXT_PUBLIC_APP_NAME,
release: process.env.NEXT_PUBLIC_APP_VERSION_RELEASE,
stage: process.env.NEXT_PUBLIC_APP_STAGE,
preset: process.env.NEXT_PUBLIC_NRN_PRESET,
},
page: {
url: location.href,
path: location.pathname,
origin: location.origin,
name: null, // XXX Will be set by the page (usually through its layout)
},
customer: {
ref: customerRef,
},
lang: lang,
locale: locale,
iframe: isInIframe,
iframeReferrer: iframeReferrer,
}}
/>
</AmplitudeProvider>
// ... elsewhere
// See https://help.amplitude.com/hc/en-us/articles/115001361248#settings-configuration-options
amplitudeInstance.init(process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY, null, {
userId,
logLevel: process.env.NEXT_PUBLIC_APP_STAGE === 'production' ? 'DISABLE' : 'WARN',
includeGclid: true,
includeReferrer: true, // See https://help.amplitude.com/hc/en-us/articles/215131888#track-referrers
includeUtm: true,
// @ts-ignore XXX onError should be allowed, see https://github.com/DefinitelyTyped/DefinitelyTyped/issues/42005
onError: (error): void => {
Sentry.captureException(error);
console.error(error); // eslint-disable-line no-console
},
sameSite: 'Strict', // 'Strict' | 'Lax' | 'None' - See https://web.dev/samesite-cookies-explained/
});
amplitudeInstance.setVersionName(process.env.NEXT_PUBLIC_APP_VERSION_RELEASE); // e.g: v1.0.0
// We're only doing this when detecting a new session, as it won't be executed multiple times for the same session anyway, and it avoids noise
if (amplitudeInstance.isNewSession()) {
// Store whether the visitor originally came from an iframe (and from where)
const visitor: Identify = new amplitudeInstance.Identify();
// XXX Learn more about "setOnce" at https://github.com/amplitude/Amplitude-JavaScript/issues/223
visitor.setOnce('initial_lang', lang); // DA Helps figuring out if the initial language (auto-detected) is changed afterwards
visitor.setOnce('initial_locale', locale);
// DA This will help track down the users who discovered our platform because of an iframe
visitor.setOnce('initial_iframe', isInIframe);
visitor.setOnce('initial_iframeReferrer', iframeReferrer);
// XXX We set all "must-have" properties here (instead of doing it in the "AmplitudeProvider", as userProperties), because react-amplitude will send the next "page-displayed" event BEFORE sending the $identify event
visitor.setOnce('customer.ref', customerRef);
visitor.setOnce('lang', lang);
visitor.setOnce('locale', locale);
visitor.setOnce('iframe', isInIframe);
visitor.setOnce('iframeReferrer', iframeReferrer);
amplitudeInstance.identify(visitor); // Send the new identify event to amplitude (updates user's identity)
}
Events - Automated page views
Below is how we automatically track all page views (through the DemoLayout
component):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<Amplitude
eventProperties={(inheritedProps): object => ({
// All app-wide properties are inherited and overloaded to track additional properties
...inheritedProps,
page: {
...inheritedProps.page,
name: pageName,
},
})}
>
{/* The event is automatically sent upon component mount */}
<LogOnMount eventType="page-displayed" />
</Amplitude>
Events - User interactions
Below is how we log events upon user interaction. (i.e: click)
When you click on the below button an event analytics-button-test-event
is sent to Amplitude.
No data will be sent if you've opted-out of analytics tracking:
You can check the event details using Amplitude Instrumentation Explorer Chrome extension.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<Amplitude>
{({ logEvent }: { logEvent: LogEvent }): JSX.Element => (
<Button
onClick={(): void => {
// eslint-disable-next-line no-console
console.log('Button click');
logEvent(AMPLITUDE_EVENTS.ANALYTIC_BUTTON_TEST_EVENT, {
action: AMPLITUDE_ACTIONS.CLICK,
});
}}
>
Click me
</Button>
)}
</Amplitude>
Your Amplitude Device ID
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<DisplayOnBrowserMount
// When using SSR, we want to render the deviceId immediately because we have access to it through server cookies
// When using SSG, we need to wait for the browser render because we don't have access to the cookies when generating the static page
// To test the different behaviours, refresh the both /examples and /products page with JS disabled
// and notice how the deviceId is properly included in the HTML with SSR (/products), unlike SSG (/examples) where it's empty
// XXX This example showcase this complex behaviour. You may want to do something similar for a "Profile" section in <Nav>,
// that can be rendered using both SSG/SSR depending on the page, where SSR should render the component but SSG should wait for browser re-render
deps={[deviceId]}
>
<code>{deviceId}</code>
</DisplayOnBrowserMount>