RUM JS API

Getting started with the SpeedCurve RUM JavaScript API (lux.js)

If you're new to RUM, make sure to check out our RUM getting started guide. To get even more out of RUM, read our API below, which explains how to:

  • adjust your sample rate

  • add custom data (such as cart size and conversion rate)

  • add page labels, so you can compare synthetic and RUM data

  • monitor SPAs

  • add custom metrics

Note: "LUX" refers to the SpeedCurve RUM JavaScript library lux.js.

LUX API

RUM works just by loading the lux.js script. Nothing else is needed. But there is an API that provides additional features as described here. All of the API is accessed through the LUX global variable.

LUX Properties

You can set these properties to alter the default behavior of RUM. Since lux.js is loaded asynchronously, use the following pattern to make sure LUX is defined before setting a property:

LUX = window.LUX || {};

LUX.auto

(boolean) default: true

If set to false, the beacon is not sent at the window load event. The reason for setting this to false is if your page is a template and the actual content is loaded after the window load event, or something else happens after the onload that you want included in the beacon. If you set LUX.auto to false, then you need to call LUX.send() in order for the beacon to be sent.

Example:

LUX = window.LUX || {};
LUX.auto = false;

LUX.label

(string) default: document.title

This is the “page label” used to group RUM data by page type. The default value is the current page title (taken from document.title). It’s a best practice to set pages to the same label used in your SpeedCurve synthetic Settings for the same page type. Maximum length is 255 characters.

Example:

LUX = window.LUX || {};
LUX.label = "home";

LUX.sendBeaconOnPageHidden

(boolean) default: true (or false when LUX.auto = false)

When this setting is true, SpeedCurve RUM will try to automatically send the beacon before the user leaves or closes the page. This is implemented by listening for for the pagehide and visibilitychange events as these are considered to be the last reliable time to save data. This setting increases the likelihood that RUM data is captured for every page.

This setting is enabled by default, unless LUX.auto = false. See the Single-page applications (SPA) documentation for more information.

LUX.trackErrors

(boolean) default: true

SpeedCurve RUM captures JavaScript errors by default. This can be disabled by setting LUX.trackErrors = false.

LUX = window.LUX || {};
LUX.trackErrors = false;

LUX.maxErrors

(number) default: 5

Control the maximum number of errors reported for a single page load.

LUX = window.LUX || {};
LUX.maxErrors = 1;

LUX.minMeasureTime

(number) default: 0

Force SpeedCurve RUM to capture data for a minimum amount of time before sending the beacon. Specified in milliseconds.

Note: LUX.sendBeaconOnPageHidden takes priority over this setting, so pages that are abandoned before the minimum measure time will still be recorded. Conversely, calling LUX.send() before the minimum measure time has elapsed will force the beacon to be sent early.

LUX = window.LUX || {};
LUX.minMeasureTime = 5000; // Capture data for at least 5 seconds

LUX.maxMeasureTime

(number) default: 60,000

Force the beacon to be sent after measuring data for a maximum amount of time. Specified in milliseconds.

Note: This is the maximum measure time. Other mechanisms that would cause a beacon to be sent e.g. the onload event or calling LUX.send() can send the beacon before this timeout is reached.

LUX = window.LUX || {};
LUX.maxMeasureTime = 30_000; // Automatically send the beacon after 30 seconds

LUX.newBeaconOnPageShow (experimental)

(boolean) default: false

This feature is experimental. Enabling it may decrease your overall metric values.

Enable tracking of pages that are restored from the back-forward cache (bfcache). This works by initializing a new page view (similar to calling LUX.init()) on bfcache restore. The beacon for this new page view is not automatically sent by lux.js, so we recommend using this setting in conjunction with LUX.sendBeaconOnPageHidden = true to send the beacon when the user leaves the page, and/or setting a lower maximum measure time to send the beacon after a timeout, e.g.LUX.maxMeasureTime = 30_000.

Pages that are restored from bfcache have their metrics measured relative to the time at which the page was restored. In many cases this will result in metrics with values that are zero. This is because from the user's perspective, the page loaded instantly.

LUX = window.LUX || {};  
LUX.newBeaconOnPageShow = true;

LUX.samplerate

(number) default: 100

This is the sample rate for determining whether the beacon is sent. You can set it to an integer from 0 to 100 inclusive as the percentage of metrics to accept. This is useful for collecting data while staying under your RUM budget of page views per month. The percentage is based on the session ID, therefore, entire sessions are accepted or rejected in whole.

A simpler way to control your sample rate is to specify it under Settings -> RUM. But you can use LUX.samplerate to have more fine-grained control (such as setting different sample rates for different countries, pages, devices, etc.).

Example:

LUX = window.LUX || {};
LUX.samplerate = 10; // Collect data for 10% of user sessions

LUX.trackHiddenPages

(boolean) default: false

Since v308 lux.js no longer tracks pages that are not visible to the user e.g. pages that have been opened in a background tab. You can force lux.js to send beacons on pages that are not visible by enabling this setting.

LUX = window.LUX || {};
LUX.trackHiddenPages = true;

LUX.cookieDomain

(boolean) default:

Controls the domain for SpeedCurve RUM's session cookies. By default, cookies are set on the current domain. You may wish to explicitly set LUX.cookieDomain to allow users to be tracked across domains. For example an online store at www.mystore.tld may host its checkout process on checkout.mystore.tld . To track users across both domains, LUX.cookieDomain would be set to "mystore.tld".

LUX = window.LUX || {};
LUX.cookieDomain = "mystore.tld";

LUX Functions

LUX.addData(name, value)

This function is used to add your own custom data to the beacon. This "custom data" can be used in the SpeedCurve UI to segment the RUM performance data. This is useful for doing A/B testing. You can also store business metrics (e.g., cart size or conversions) and plot those along with performance metrics to see correlations.

The maximum allowable size of all name value pairs concatenated is 500 characters.

See Custom Data for more information.

Example:

// Custom data values can be strings, numbers, or booleans
LUX.addData('cartsize', 128);
LUX.addData('experiment A', 'control');
LUX.addData('loggedin', true);

// Custom data can be removed by setting the value to null or undefined
LUX.addData('username', null);
LUX.addData('username', undefined);

LUX.forceSample()

This function is incredibly handy if you have set a RUM sample rate and are trying to debug RUM on your site. Imagine that your sample rate is 1% and you're loading one of your pages in the browser to see whether RUM data is generated. 99% of the time no RUM data is generated, but you won't know if that's because RUM isn't working or you're not being sampled.

In RUM, sampling is based on your session cookie. So the only way to make sure you get sampled is to change your cookie. Calling LUX.forceSample() changes your session cookie so that on all subsequent page views you will be sampled and should see RUM data being generated. Note that session cookies expire after 24 hours or 30 minutes of inactivity, so you might have to call LUX.forceSample() again.

Example:

LUX.forceSample()

LUX.getDebug()

Returns debug data for the current session. The debug data is returned in an encoded format, so you will need to use the RUM Debug Parser to interpret the data. Hint: run copy(LUX.getDebug()) in your browser console to copy the results directly to your clipboard.

If you're experiencing problems getting RUM working on your site, we recommend using LUX.getDebug() in the browser console as a first step to get more insight into what is, and is not, happening. Also see LUX.forceSample().

Example:

LUX.getDebug()

// Copy the debug logs to the clipboard
copy(LUX.getDebug())

LUX.init()

RUM works automatically for normal ("Web 1.0") pages. But if your site is a Single Page App (SPA) then you need to add code to initialize RUM at the beginning of each SPA "page view" and send the beacon at the end. Call this function at the beginning of the SPA page transition. See the Single-page applications (SPA) documentation for more information.

Example:

LUX.init();
// start SPA page transition

LUX.mark(markName)

or LUX.mark(markName, markOptions)

Record the timestamp of a performance event. This will be recorded as a custom user timing metric in SpeedCurve.

This function is a wrapper around the native performance.mark and can be called in the same way. It provides a polyfill for older browsers that don't support the User Timing spec. Maximum length for markName is 128 characters.

Example:

LUX.mark("beforeJson");
LUX.mark("customStarTime", { startTime: 200 });

LUX.measure(measureName, startMark, endMark)

or LUX.measure(measureName, measureOptions)

Record the duration between two performance events. This will be recorded as a custom user timing metric in SpeedCurve. In a SPA, if no startMark is provided, the timestamp of the last LUX.init() call will be used as the start mark.

This function is a wrapper around the native performance.measure and can be called in the same way. It also provides a polyfill for older browsers that don't support the User Timing spec. Maximum length for measureName is 128 characters.

Example:

LUX.measure("jsonLoadTime", "beforeJson");
LUX.measure("jsonLoadTime", "beforeJson", "afterJson");
LUX.measure("customDuration", { start: "beforeJson", duration: 450 });

LUX.send()

Manually send the data that has been collected for the current page view. This function is normally used along with LUX.init() to measure single page applications (SPAs). It can also be used in normal pages to halt data collection and send the page data before the onload event.

If LUX.markLoadTime() has not already been called, the load time will be recorded as the time that LUX.send() was called.

Example:

// end of SPA page transition
LUX.send();

LUX.markLoadTime()

Record when a single page application (SPA) page has finished loading. This will be used as the "Load Time" metric in SpeedCurve dashboards.

If possible this function should be called after all data has loaded and the page has been fully rendered, however in some circumstances it may be sufficient to call it during the "mounted" lifecycle callback of various frameworks (componentDidMount in React, mounted in Vue v2, etc).

Note that this function is ignored for "regular" (non-SPA) page views that have an onload time.

fetch("/page-data").then((response) => {
  response.json().then((pageData) => {
    renderPage(pageData);
    LUX.markLoadTime();
  });
});

LUX HTML Attributes

data-sctrack

When the first interaction with the page is a click or key press, SpeedCurve RUM attempts to identify the element that was interacted with. The data-sctrack attribute can be used to override the normal element identification. If an element or any of its ancestors has the data-sctrack attribute, SpeedCurve RUM will use the value of this attribute as the interaction name.

If no data-sctrack attribute is found on the interaction element or any of its ancestors, SpeedCurve RUM will build a DOM selector by visiting every parent element until the selector is a maximum of 100 characters long or an id attribute is found on an ancestor.

In the example markup below, the following interaction names would be chosen:

  • For the Home link, the interaction name would be index (from the data-sctrack attribute).
  • For the Shop and Search links, the interaction name would be navbar (from the ancestor's data-sctrack attribute).
  • For the Go Back button, the interaction name would be back (from the id attribute).
  • For the Go Forward button, the interaction name would be div.container > div.controls > button.
<div class="container">
  <a href="/" id="home" data-sctrack="index">Home</a>
  
  <ul data-sctrack="navbar">
    <li> <a href="/shop" id="shop">Shop</a>
    <li> <a href="/search">Search</a>
  </ul>
  
  <div class="controls">
    <button id="back">Go Back</button>
    <button>Go Forward</button>
  </div>
</div>