Tracking in Single-Page Applications
Able Customer Data Platform simplifies implementation of tracking in modern JavaScript applications. Able replaces multiple APIs that need to be integrated on both front-end and back-end, providing a convenient way to streamline implementation of capturing Google Analytics, Google Ads and Facebook (Meta) Ads tracking ids, associating them with leads and users and sending conversions to the respective APIs when they occur on the back-end.
This page contains instructions for custom scripting of page view and browser event tracking in web applications that use single-page application frameworks such as React.JS, Vue.JS, Next.js.
For most non-SPA funnels, the 'Get Code' section in the Able CDP Dashboard can be used to generate tracking code automatically, instead of the manual instructions. It also has a code snippet (on the 'Web application' tab) that can be used to track OAuth/SSO logins.
If the sign-up is implemented using Stripe payment links, tracking of the application itself is usually not necessary and the Stripe payment link tracking guide should be used instead.
Introduction to Tracking in Single-Page Applications
Single-page JavaScript applications present unique challenges to tracking user activity. Primarily, because loading of the state happens asynchronously, browser tracking cannot be implemented in the usual way as the page elements are recreated dynamically throughout the state changes.
While in regular web applications, Able either tracks static sign-up forms or receives identifying messages after a single sign-on or OAuth login is complete, SPA applications require creating a simple component that communicates the application state to Able.
One of the major benefits of such an approach is that it allows tracking conversions in web applications without using cookies, relying solely on volatile browser state and explicit consent provided by a customer when signing up. The subsequent conversions are then tracked and attributed to the source entirely server-side, without relying on browser.
A minimal implementation of SPA tracking consists of two primary steps:
- recording source of the visitor and click identifiers when the page loads
- associating visitor with a user sign-up, lead form completion, or checkout
The subsequent instructions are using React/Next to demonstrate the concepts of tracking single-page applications with a customer data platform. They can be adapted to other frameworks such as Vue.js as needed.
Tracking Page Views
In order to record source of the visitor, the application should load Able tracking code and send a tracking event to it.
This can be achieved by including the following component from a high-level component present on all landing pages (such as layout.tsx/.jsx):
'use client';
import Script from "next/script";
export default function TrackingScripts() {
/**
* Init tracking code and track page view
*/
function trackPageView() {
window.uipeParams = { setid: true };
window.uipe('init', '%%FUNNEL_MARK%%');
window.uipe('track', 'PageView');
}
return <Script
type="text/javascript"
src="https://app.ablecdp.com/ue.js"
onLoad={() => trackPageView()}
/>
}
Where %%FUNNEL_MARK%% - is the unique code of your Able CDP funnel, available in the bottom of the 'Get Code' page in Able Dashboard.
This code implements several measures to ensure that the landing page view is tracked correctly.
First, it tells Next.js that it must run in browser (use client) and uses Next.js <Script /> component to render <script> tag on the page.
The browser then loads the tracking script, which initializes a global function 'uipe' and calls the trackPageView() function.
The function initializes Able tracking on the page. The initialization consists of two steps.
First, it sets 'uipeParams' global variable, to indicate that it should assign a unique identifier to the current SPA session ('setid'). This identifier isn't persisted in the browser, but is available to an SPA during the entire application lifecycle.
Able doesn't persist this identifier or store anything in the browser storage by default, expecting that the entire journey would happen within a session or third-party tracking cookies (such as set by Google Analytics) will be available.
This behavior can be modified by enabling the 'storeid' parameter, which will store the identifier in the LocalStorage:
window.uipeParams = { setid: true, storeid: true };
It then calls the 'init' function of the tracking code to set the funnel identifier and tracks PageView event.
The PageView event would record:
- landing page URL
- referrer URL
- UTM parameters
- paid click id parameters
- first-party browser identifiers, such as set by Google Analytics and Meta Pixel
Note: if, instead of tracking landing page view with its own identifier as soon as possible, you'd instead like Able to ensure each PageView event is tracked with a Google Analytics Client ID, omit the
window.uipeParams = { ... }
string. This will make Able's tracking code to wait for the Google Analytics to be initialized and the Google Analytics Client ID to be available on the page.
Tracking Forms
Due to the nature of single-page applications, automatic and secure tracking of sign-ups in them requires integrating tracking code manually. This is necessary to inform Able when a user signs up so that the email or another user identifier can be linked with the browser session, enabling the attribution of further conversions to the customer's source.
In a simple scenario, user would enter their contact details in a form manually. This is often the case with lead and sign-up forms on marketing websites, e-commerce checkouts and similar forms.
In such case, activity can be tracked by adding a single function call to the form handler. This will associate visitor's identity with customer details in Able. For example:
window.uipe('track', 'Lead', {
keys: {
email: customer.email,
phone: customer.phone,
},
lead: {
firstName: customer.firstName,
lastName: customer.lastName
},
});
See the Send Events Using Webhooks document for detailed description of possible keys and other event parameters. Any suitable event name such as 'Identify', 'Login' etc can be used instead of Lead in the example above.
Tracking Logins and Sign-ups
In applications that use a single-sign-on widget, such as provided by Google or Meta, users don't enter their emails manually, and consequently, they cannot be tracked using form tracking.
In order to track such sign-ups, the application should:
- Retrieve user identity from server after a sign-up or log in. This is typically done via an API call, however the user identity can also be passed to browser in a <meta> tag or as a global JavaScript variable.
- Once user identity is obtained, send a tracking event to Able.
To associate user details with the visitor, this event needs to be tracked in a browser. Alternatively, you could record one of the browser identifiers, such as the Google Analytics Client ID, and send it to Able along with the customer's email in a server-side event.
Such tracking would typically be executed from a useEffect hook. This ensures that it doesn't interfere with the SPA framework state updates.
A common mistake when using useEffect is ignoring reactive dependencies. This can lead to a race condition resulting in many visitors not being tracked correctly as their identity may not yet load from a server, and in sending redundant events to Able when unrelated state updates occur.
A complete tracking code therefore may look as follows:
'use client';
import { useEffect, useState } from "react";
import Script from "next/script";
export default function TrackingScripts() {
const [uipeReady, setUipeReady] = useState();
const [currentUser, setCurrentUser] = useState();
/**
* Init tracking code and track page view
*/
function trackPageView() {
window.uipeParams = { setid: true };
window.uipe('init', '%%FUNNEL_MARK%%');
window.uipe('track', 'PageView');
setUipeReady(true);
}
useEffect(() => {
/* retrieve user identity and call setCurrentUser */
}, []); // [] ensures this block is only run once
useEffect(() => {
// both tracking script and the user identity
// must be present before continuing
if (!uipeReady || !currentUser)
return;
window.uipe('track', 'Lead', {
keys: {
email: currentUser.email,
client_id: currentUser.id,
}
});
}, [
uipeReady,
currentUser
]); // run each time uipeReady or currentUser updates
return <Script
type="text/javascript"
src="https://app.ablecdp.com/ue.js"
onLoad={() => trackPageView()}
/>
}
Depending on your sign-up flow, this event can be sent either:
- When a customer leaves contact details, such as by submitting a lead form or signing up.
- When a customer accesses the application for the first time. This is required if you're using a single sign-on and some users sign-up without ever entering their email in a form.
While form tracking is used primarily to associate customer details with the visitor, Able also offers integrations with CRMs and marketing automation platforms such as ActiveCampaign and Salesforce, allowing to create contacts there when a new lead form is submitted without having to build own API integration. When doing so, Able populates custom attributes such as referrer URL and UTM tags using built-in first-party attribution.
If you have a standalone marketing website in addition to a SPA application, install tracking code using the instructions on Track Website Visitors and Forms page to track the entire funnel.
If standalone marketing website doesn't have a sign-up form, but has landing pages linking to the sign-up form within the applications, make sure that Google Analytics cross-domain is set up between the marketing website and the application domains. Able uses Google Analytics Client Id to track visitors between domains, including for our built-in attribution and other integrations such as Google Ads and Facebook Ads.
Please contact Able CDP support if you need any help with the form tracking set up.
Tracking Forms on Third-Party Websites
In many cases, such as when tracking Stripe payment links, a unique identifier is passed as a URL parameter to a website that contains the form (Stripe-hosted checkout, in this case). To correctly track links to third-party checkouts in JavaScript applications, including those using server-side rendering with subsequent hydration (transparently attaching dynamic SPA code to the server-side rendered elements, as in Next.js framework):
- Append an identifier to the links.
- Generate a tracking event on the website containing the link to notify Able that the current visitor has a given identifier. By convention, this is done by specifying it as the 'client' key.
For Stripe, append a client_reference_id
parameter to the links and pricing tables.
<a href="https://buy.stripe.com/...
?client_reference_id={clientId}"></a>
<stripe-pricing-table client-reference-id={clientId} />
How to generate the value:
- If using the
setid
option as described above:- Get the ID from
window.uipeCookielessId
, and prefix it with 'cl:' or 'cl-' (Stripe requires 'cl-' as colons are not permitted in client identifiers). - Note: SPA state won't be automatically updated when
window.uipeCookielessId
is set. Pass a state that changes when Able's tracking script executes (or afteruipe('track', 'PageView')
is called) before using this value.
- Get the ID from
- If not using the
setid
option:- Generate a custom ID, which can be: a. A persistent identifier for a logged-in customer in your system b. A session identifier c. A random UUID generated when a checkout link or form is shown
After generating the ID, send an event. The event type typically reflects the action corresponding to the generation (e.g., PaymentLinkShown
, Identify
). Alternatively, add this parameter to the keys
value sent with the PageView
event.
Example event to be sent after ID generation:
window.uipe('track', 'PaymentLinkShown', {
keys: {
"email": "john@aol.com",
"client_id": "12354"
}
});
Tracking Purchases
The greatest benefit Able provides is the ability to attribute purchases that happen on the back-end to front-end visitors, Google Analytics Click IDs, Google Ads and Facebook (Meta) click IDs, as well as integrate with respective conversion APIs.
Able is already integrated with popular order processing back-ends such as Stripe and WooCommerce. Alternatively, events can be sent from your own back-end to Able REST API.
Able will attribute them to known customer details automatically and will send conversion events to the external services' APIs.