Rokt UX Helper - Advanced Usage
This guide covers advanced usage scenarios and features of the Rokt UX Helper library.
Session API (v2) HelpersDirect link to Session API (v2) Helpers
The UX Helper is a renderer, not a network client. It never calls Rokt. Your backend owns every HTTP request, and the library converts between the DOM shapes and the wire shapes at each boundary.
If your backend calls the Session API (/v2/sessions/*) rather than the Experiences API, two named exports cover that conversion for you: adaptSelectResponse on the way in, and buildRecordEventsRequest on the way out. Both are additive, so the RoktUXEvent and RoktPlatformEvent payloads are unchanged whichever API you call.
Each helper sits at one endpoint, and the schemas on both sides of it are in the Session API reference:
| Helper | Endpoint | Converts |
|---|---|---|
adaptSelectResponse | POST /v2/sessions/offers | Offers response into a render model |
buildRecordEventsRequest | POST /v2/sessions/events | Platform event into an events body |
Keep the actual POST calls on your server. rpub and rsec are server credentials and must never reach the browser.
adaptSelectResponseDirect link to adaptselectresponse
The /v2/sessions/offers response is snake_case. renderExperiences() expects the camelCase model, so convert it first:
import '@rokt/ux-helper-web';
import { adaptSelectResponse } from '@rokt/ux-helper-web';
// Your own backend call. This library does not make it for you.
const offersResponse = await fetchV2Offers();
const model = adaptSelectResponse(offersResponse);
document.querySelectorAll('rokt-layout-view').forEach((layoutView) => {
layoutView.renderExperiences(model);
});
The conversion is structure-aware rather than a blanket key rewrite:
- Layout schema strings (
outer_layout_schema,layout_variant_schema,child_layout_schema) are opaque and pass through byte for byte. - Keyed maps such as
responseOptionsMap,images,copyandmetadatakeep their original keys, so a creative key likecreative.titleis not mangled. - Only known wire fields are renamed, so nothing inside your creative content is touched.
successis derived from whether any plugins came back, so an emptypluginsarray (a valid no-fill) renders nothing rather than erroring.options.useDiagnosticEventsis set, so render diagnostics are reported.
buildRecordEventsRequestDirect link to buildrecordeventsrequest
RoktPlatformEvent carries the V1 /events payload, which can be posted as is on V1. For the Session API, convert it to a RecordEventsRequest:
import { buildRecordEventsRequest } from '@rokt/ux-helper-web';
// The four event types the Session API documents for server-to-server.
const S2S_EVENT_TYPES = new Set(['impression', 'viewed', 'signal_response', 'dismissal']);
document.addEventListener('RoktPlatformEvent', async (event) => {
const body = buildRecordEventsRequest(event.detail, {
channelType: 's2s',
singleSession: true
});
// The helper maps the full wsdk event set, which is wider than the s2s
// contract, so keep only the documented types.
body.events = body.events.filter((txnEvent) => S2S_EVENT_TYPES.has(txnEvent.event_type));
if (body.events.length === 0) {
return; // the request needs at least one event
}
// Every event needs an instance_id. The helper sets one only when the platform
// event carried a clientUniqueId, so fill the gaps here. Generate it at
// conversion time, not per attempt, so a resend deduplicates rather than
// recording twice.
body.events.forEach((txnEvent) => {
txnEvent.instance_id ??= crypto.randomUUID();
});
await postV2Events(body); // your own backend call, which posts to /v2/sessions/events
});
OptionsDirect link to Options
The defaults suit the helper's own wsdk channel. On a server-to-server credential set both explicitly, as above: /v2/sessions/events takes s2s for channel.type and requires single_session on the request.
| Option | Default | Description |
|---|---|---|
channelType | 'wsdk' | Sets channel.type. Use the channel your credential is provisioned for, which for this guide is s2s. |
singleSession | omitted | Sets single_session. Left off the body entirely when not provided, so pass true. |
channel.sdk_version is taken from payload.integration.version and omitted if that is absent.
Event type mappingDirect link to Event type mapping
| Platform event | event_type |
|---|---|
SignalImpression | impression |
SignalViewed | viewed |
SignalInitialize | signal_initialize |
SignalResponse | signal_response |
SignalDismissal | dismissal |
SignalActivation | user_interaction |
SignalCartItemQuantitySelected | cart_item_quantity_selected |
SignalActivation also gets data.interactionType: "activation", since v2 models interactions as one type with a discriminator.
Anything not in this table is dropped from the batch rather than sent through unmapped, including SignalSdkDiagnostic. Diagnostics travel on their own path.
The table covers the helper's own wsdk channel, which carries more event types than the server-to-server contract does. The Session API reference documents impression, viewed, signal_response and dismissal. The other three, signal_initialize, user_interaction and cart_item_quantity_selected, sit outside that contract and come back as per-event validation errors on an s2s credential, which is why the example above filters the batch down to the four.
Field mappingDirect link to Field mapping
clientUniqueIdbecomesinstance_id, andsessionIdbecomessession_id.eventTimebecomes an epoch-millisecondtimestamp. Values outside the year range [2000, 2100] are dropped so the gateway falls back to receive-time, because one out-of-range timestamp rejects the whole batch.metadataentries move intodata.clientTimeStampis dropped since it is promoted totimestamp, andcaptureMethodbecomescapture_method.eventDatais parsed if it is a JSON object and merged intodata. Non-JSON is ignored rather than corrupting the batch.parentGuid,pageInstanceGuidandcreativeInstanceGuidbecomedata.parent_id,data.page_instance_guidanddata.creative_instance_guid. These are written last, so routing fields win over any colliding attribute.- Empty objects are omitted throughout rather than sent as
{}.
Running the example platform event payload from the Web UX Helper guide through buildRecordEventsRequest produces:
{
"channel": { "type": "s2s", "sdk_version": "1.0" },
"events": [
{
"event_type": "impression",
"session_id": "b23d004d-b2e6-43e9-b254-d9193e650000",
"timestamp": 1733373774683,
"data": {
"capture_method": "ClientProvided",
"parent_id": "9b6f0e5b-621d-4597-8a73-71d6a5b43a74",
"page_instance_guid": "b23d004d-b2e6-4b82-b7a2-84a13b6c57c5"
}
}
],
"single_session": true
}
What you add before postingDirect link to What you add before posting
The helper converts what the platform event carries, which is not everything /v2/sessions/events requires. Two fields it cannot derive:
instance_id— absent above because that example event carries noclientUniqueId. Generate a UUID per event, as the example does.data.token— the event token for the element named byparent_id. It comes from the offers response, so look it up on your server, where you already hold that response, and add it todata.
Both are required by the Event schema, so a converted body posted untouched is rejected.
Multiple Placement SupportDirect link to Multiple Placement Support
Rokt UX Helper supports multiple placements on a single page. Each placement requires its own rokt-layout-view element with a unique ID:
<!-- Primary placement -->
<rokt-layout-view id="rokt-primary"></rokt-layout-view>
<!-- Secondary placement -->
<rokt-layout-view id="rokt-secondary"></rokt-layout-view>
<!-- Overlay placement -->
<rokt-layout-view id="rokt-overlay" render-overlay></rokt-layout-view>
When rendering experiences, each placement will only display plugins that match its selector:
// Get the experience data that contains multiple plugins
const experienceData = await fetchExperienceData();
// Render experiences to all placement elements
document.querySelectorAll('rokt-layout-view').forEach(element => {
element.renderExperiences(experienceData);
});
Element TargetingDirect link to Element Targeting
Plugins in the experience payload are matched to the appropriate rokt-layout-view element based on the targetElementSelector property in the plugin configuration. The matching rules are:
- ID Selector: A plugin with
targetElementSelector: "#rokt-primary"will target the element withid="rokt-primary". - Class Selector: A plugin with
targetElementSelector: ".placement-class"will target elements withclass="placement-class". - Body Selector: A plugin with
targetElementSelector: "body"will target elements with therender-overlayattribute.
Event CommunicationDirect link to Event Communication
Sending Events to LayoutsDirect link to Sending Events to Layouts
You can also send events to the rendered layouts using the send method. This is useful for communicating with layouts based on actions in your application:
// Send a cart update event to all rendered layouts
await roktElement.send('V2_UPDATE_CART_ITEM', {
cartItemId: "item-123",
quantity: 2
});
This is useful for dynamic updates to rendered experiences based on user interactions or external data changes.
Handling Events from All PlacementsDirect link to Handling Events from All Placements
To centralize event handling, you can listen for events at the document level:
document.addEventListener('RoktUXEvent', (event) => {
const { pluginId, eventName, data } = event.detail;
console.log(`UX Event from plugin ${pluginId}:`, eventName, data);
});
document.addEventListener('RoktPlatformEvent', (event) => {
// Forward all platform events to your backend
fetch('/api/rokt-events', {
method: 'POST',
body: JSON.stringify(event.detail)
});
});
Cleanup and Error HandlingDirect link to Cleanup and Error Handling
Proper CleanupDirect link to Proper Cleanup
When a component containing rokt-layout-view is removed, ensure proper cleanup:
// In vanilla JS
function cleanup() {
const roktElement = document.getElementById('rokt-placement');
if (roktElement) {
roktElement.close();
}
}
// In React
useEffect(() => {
return () => {
if (roktRef.current) {
roktRef.current.close();
}
};
}, []);
Error HandlingDirect link to Error Handling
Implement proper error handling when working with the Rokt UX Helper:
try {
const experienceData = await fetchExperienceData();
if (!experienceData || !experienceData.plugins || experienceData.plugins.length === 0) {
console.warn('No experiences to render');
return;
}
roktElement.renderExperiences(experienceData);
} catch (error) {
console.error('Failed to render Rokt experiences:', error);
// Implement fallback behavior if needed
}
// Listen for layout failures
roktElement.addEventListener('RoktUXEvent', (event) => {
if (event.detail.eventName === 'LayoutFailure') {
console.error('Layout failed to render:', event.detail);
// Implement fallback behavior
}
});
Performance ConsiderationsDirect link to Performance Considerations
For optimal performance when using Rokt UX Helper:
- Lazy Loading: Consider lazy loading the Rokt UX Helper library, especially if the experiences are not visible immediately:
// Load the library only when needed
function loadRoktUXHelper() {
return import('@rokt/ux-helper-web').then(() => {
console.log('Rokt UX Helper loaded');
// Initialize and render experiences
});
}
// Use with Intersection Observer for visibility-based loading
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
loadRoktUXHelper();
observer.disconnect();
}
}, { threshold: 0.1 });
observer.observe(document.getElementById('rokt-container'));