Skip to main content
Tracking user behavior is essential for Constructor.io to deliver personalized search results, relevant recommendations, and actionable analytics. The JavaScript Client SDK provides a comprehensive tracking system that captures user interactions automatically or manually.

Overview

The tracking system consists of three main components:
  1. Tracker Module - Methods for tracking specific user events
  2. Request Queue - Batches and sends tracking events efficiently
  3. Event Dispatcher - Emits custom DOM events for tracking lifecycle

Enabling Tracking

Tracking is disabled by default. To enable it, set sendTrackingEvents: true during initialization:
import ConstructorIO from '@constructor-io/constructorio-client-javascript';

const constructorClient = new ConstructorIO({
  apiKey: 'YOUR_API_KEY',
  sendTrackingEvents: true // Enable tracking
});
Without enabling sendTrackingEvents, none of your tracking method calls will send data to Constructor.io. Always verify this is set to true in production.

How Tracking Works

Request Queue

When you call a tracking method, the event is added to a request queue instead of being sent immediately. This approach:
  • Batches events to reduce network requests
  • Persists events in localStorage to prevent data loss
  • Handles offline scenarios by queuing events until connectivity returns
  • Prevents bot traffic using humanity checks
// Event is queued, not sent immediately
constructorClient.tracker.trackSearchResultClick('shoes', {
  itemId: 'shoe-123',
  itemName: 'Running Shoes'
});

Send Delay

By default, the queue waits 250 milliseconds before sending events. This delay allows multiple rapid events (like typing in search) to be batched together:
const constructorClient = new ConstructorIO({
  apiKey: 'YOUR_API_KEY',
  sendTrackingEvents: true,
  trackingSendDelay: 250 // Default: 250ms
});
Set trackingSendDelay: 0 to send events immediately without batching:
trackingSendDelay: 0 // Send immediately

Storage and Persistence

Events are stored in localStorage under the key _constructorio_requests. This ensures:
  • Events survive page refreshes
  • Events are retried if requests fail
  • Stale events (older than 3 minutes) are automatically cleared

Core Tracking Methods

All tracking methods are accessed through constructorClient.tracker:

Session Events

Records when a user session begins. Typically called once per session.
constructorClient.tracker.trackSessionStart();
Parameters:
  • networkParameters (optional): { timeout: number }
Returns: true or Error
Records when a user focuses on a search input field.
constructorClient.tracker.trackInputFocus();
Parameters:
  • networkParameters (optional): { timeout: number }
Returns: true or Error

Search Events

Records when a user submits a search query.
constructorClient.tracker.trackSearchSubmit('running shoes', {
  originalQuery: 'run sho',
  groupId: 'mens-athletic',
  displayName: 'Athletic Footwear'
});
Parameters:
  • term (required): The search term submitted
  • parameters (required): Object containing:
    • originalQuery (required): The autocomplete query that led to this search
    • groupId (optional): Group identifier if searching within a group
    • displayName (optional): Display name of the group
    • analyticsTags (optional): Additional analytics data
  • networkParameters (optional): { timeout: number }
Returns: true or Error
Records when search results are displayed to the user.
constructorClient.tracker.trackSearchResultsLoaded('running shoes', {
  resultCount: 245,
  items: [
    { itemId: 'shoe-123' },
    { itemId: 'shoe-456' },
    { itemId: 'shoe-789' }
  ],
  url: 'https://example.com/search?q=running+shoes',
  resultPage: 1,
  sortBy: 'relevance',
  sortOrder: 'descending'
});
Parameters:
  • searchTerm (required): The search query
  • parameters (required): Object containing:
    • url (required): Current page URL
    • items (required): Array of item objects with itemId (max 100 items)
    • resultCount (optional): Total number of results
    • resultPage (optional): Current page number
    • resultId (optional): Result identifier from Constructor.io response
    • selectedFilters (optional): Applied filters
    • sortOrder (optional): 'ascending' or 'descending'
    • sortBy (optional): Sort method used
    • section (optional): Index section (e.g., “Products”)
    • analyticsTags (optional): Additional analytics data
  • networkParameters (optional): { timeout: number }
Returns: true or Error
Records when a user clicks on a search result.
constructorClient.tracker.trackSearchResultClick('running shoes', {
  itemName: 'Nike Air Zoom',
  itemId: 'shoe-123',
  variationId: 'shoe-123-blue-10',
  resultId: '019927c2-f955-4020-8b8d-6b21b93cb5a2',
  section: 'Products'
});
Parameters:
  • term (required): The search query that produced this result
  • parameters (required): Object containing:
    • itemName (required): Product name
    • itemId (required): Product identifier
    • variationId (optional): Product variation identifier
    • resultId (optional): Result identifier from Constructor.io response
    • section (optional): Index section
    • analyticsTags (optional): Additional analytics data
    • slCampaignId (optional): Sponsored listing campaign ID
    • slCampaignOwner (optional): Sponsored listing campaign owner
  • networkParameters (optional): { timeout: number }
Returns: true or Error

Autocomplete Events

Records when a user selects an autocomplete suggestion.
constructorClient.tracker.trackAutocompleteSelect('running shoes', {
  originalQuery: 'run sho',
  section: 'Products',
  tr: 'click',
  itemId: 'shoe-123',
  groupId: 'mens-athletic'
});
Parameters:
  • term (required): The selected autocomplete term
  • parameters (required): Object containing:
    • originalQuery (required): The partial query that triggered autocomplete
    • section (required): Section of the selected item
    • tr (optional): Trigger type ('click', 'enter', etc.)
    • itemId (optional): Item identifier if selecting a product
    • groupId (optional): Group identifier
    • displayName (optional): Display name of the group
    • slCampaignId (optional): Sponsored listing campaign ID
    • slCampaignOwner (optional): Sponsored listing campaign owner
  • networkParameters (optional): { timeout: number }
Returns: true or Error

Product Events

Records when a user views a product detail page.
constructorClient.tracker.trackItemDetailLoad({
  itemName: 'Nike Air Zoom',
  itemId: 'shoe-123',
  variationId: 'shoe-123-blue-10',
  url: 'https://example.com/products/shoe-123'
});
Parameters:
  • parameters (required): Object containing:
    • itemName (required): Product name
    • itemId (required): Product identifier
    • url (required): Current page URL
    • variationId (optional): Product variation identifier
    • analyticsTags (optional): Additional analytics data
  • networkParameters (optional): { timeout: number }
Returns: true or Error

Conversion Events

Records conversion events like add-to-cart, add-to-wishlist, or custom actions.
constructorClient.tracker.trackConversion('running shoes', {
  itemId: 'shoe-123',
  itemName: 'Nike Air Zoom',
  variationId: 'shoe-123-blue-10',
  revenue: 129.99,
  type: 'add_to_cart',
  section: 'Products'
});
Parameters:
  • term (optional): Search term that led to the conversion
  • parameters (required): Object containing:
    • itemId (required): Product identifier
    • itemName (optional): Product name
    • variationId (optional): Product variation identifier
    • revenue (optional): Sale or retail price
    • type (optional): Conversion type (default: 'add_to_cart')
    • isCustomType (optional): Whether this is a custom conversion type
    • displayName (optional): Display name for custom type
    • section (optional): Index section (default: 'Products')
  • networkParameters (optional): { timeout: number }
Returns: true or Error
Records completed purchases (typically on order confirmation page).
constructorClient.tracker.trackPurchase({
  items: [
    { itemId: 'shoe-123', price: 129.99, count: 1 },
    { itemId: 'sock-456', price: 12.99, count: 2 }
  ],
  revenue: 155.97, // Subtotal excluding tax/shipping
  orderId: 'ORDER-789',
  section: 'Products'
});
Parameters:
  • parameters (required): Object containing:
    • items (required): Array of item objects (max 100), each with:
      • itemId (required): Product identifier
      • price (optional): Item price
      • count (optional): Quantity purchased
      • itemName (optional): Product name
      • variationId (optional): Variation identifier
    • revenue (required): Total order subtotal (excluding tax, shipping)
    • orderId (optional): Unique order identifier
    • section (optional): Index section (default: 'Products')
    • analyticsTags (optional): Additional analytics data
  • networkParameters (optional): { timeout: number }
Returns: true or Error
The SDK prevents duplicate purchase events by tracking orderId values. If you call trackPurchase with the same orderId twice, the second call will be ignored.

Recommendation Events

Records when recommendation results are displayed.
constructorClient.tracker.trackRecommendationView({
  podId: 'home_page_recs',
  numResultsViewed: 4,
  items: [
    { itemId: 'shoe-123' },
    { itemId: 'shoe-456' },
    { itemId: 'shoe-789' },
    { itemId: 'shoe-012' }
  ],
  url: 'https://example.com',
  resultId: '019927c2-f955-4020-8b8d-6b21b93cb5a2'
});
Parameters:
  • parameters (required): Object containing:
    • podId (required): Recommendation pod identifier
    • numResultsViewed (required): Number of recommendations viewed
    • url (required): Current page URL
    • items (optional): Array of viewed items (max 100)
    • resultCount (optional): Total number of recommendations
    • resultPage (optional): Page number
    • resultId (optional): Result identifier from Constructor.io response
    • section (optional): Index section (default: 'Products')
    • analyticsTags (optional): Additional analytics data
    • seedItemIds (optional): Seed item(s) used for recommendations
  • networkParameters (optional): { timeout: number }
Returns: true or Error
Records when a user clicks on a recommendation.
constructorClient.tracker.trackRecommendationClick({
  podId: 'home_page_recs',
  strategyId: 'alternative_items',
  itemId: 'shoe-123',
  itemName: 'Nike Air Zoom',
  variationId: 'shoe-123-blue-10',
  resultId: '019927c2-f955-4020-8b8d-6b21b93cb5a2'
});
Parameters:
  • parameters (required): Object containing:
    • podId (required): Recommendation pod identifier
    • strategyId (required): Recommendation strategy identifier
    • itemId (required): Product identifier
    • itemName (required): Product name
    • variationId (optional): Product variation identifier
    • resultId (optional): Result identifier from Constructor.io response
    • section (optional): Index section (default: 'Products')
    • analyticsTags (optional): Additional analytics data
    • seedItemIds (optional): Seed item(s) used for recommendations
  • networkParameters (optional): { timeout: number }
Returns: true or Error

Event Dispatcher

The Event Dispatcher emits custom DOM events throughout the tracking lifecycle. These events can be used for debugging, analytics, or triggering custom behavior.

Emitted Events

All events are dispatched on the window object with the cio.client. prefix:
// Listen for client instantiation
window.addEventListener('cio.client.instantiated', (event) => {
  console.log('Constructor.io client initialized:', event.detail);
});

// Listen for beacon loaded (if using legacy beacon)
window.addEventListener('cio.beacon.loaded', () => {
  console.log('Constructor.io beacon loaded');
});

Configuration

Control event dispatcher behavior during initialization:
const constructorClient = new ConstructorIO({
  apiKey: 'YOUR_API_KEY',
  eventDispatcher: {
    enabled: true,        // Enable/disable event dispatching
    waitForBeacon: false  // Don't wait for legacy beacon
  }
});
enabled
boolean
default:true
Enable or disable custom event dispatching on the window object.
waitForBeacon
boolean
default:true
If true, the dispatcher waits for the cio.beacon.loaded event before dispatching queued events. This is for compatibility with legacy Constructor.io beacon implementations.Set to false if you’re not using the legacy beacon.

Referrer Tracking

By default, Constructor.io includes referrer information with tracking events:
const constructorClient = new ConstructorIO({
  apiKey: 'YOUR_API_KEY',
  sendTrackingEvents: true,
  sendReferrerWithTrackingEvents: true // Default
});
Referrer data includes:
  • Current page URL (host + pathname)
  • UTM parameters from the URL
  • Document referrer (previous page)
This helps Constructor.io understand user navigation patterns and attribute conversions to traffic sources.

Humanity Check

The SDK automatically filters bot traffic to ensure tracking data quality:
  • Checks user agent against a bot list
  • Stores humanity check result in sessionStorage or localStorage
  • Prevents bot events from being queued
const constructorClient = new ConstructorIO({
  apiKey: 'YOUR_API_KEY',
  humanityCheckLocation: 'session' // or 'local'
});

Best Practices

1. Enable Tracking in Production

const constructorClient = new ConstructorIO({
  apiKey: process.env.CONSTRUCTOR_API_KEY,
  sendTrackingEvents: process.env.NODE_ENV === 'production'
});

2. Track Complete User Journeys

Implement tracking at key touchpoints:
// On search results page
constructorClient.tracker.trackSearchResultsLoaded(query, { items, resultCount });

// When user clicks a result
constructorClient.tracker.trackSearchResultClick(query, { itemId, itemName });

// On product detail page
constructorClient.tracker.trackItemDetailLoad({ itemId, itemName, url });

// When user adds to cart
constructorClient.tracker.trackConversion(query, { itemId, revenue, type: 'add_to_cart' });

// On order confirmation
constructorClient.tracker.trackPurchase({ items, revenue, orderId });

3. Include Result IDs

Always pass resultId from Constructor.io responses to tracking methods:
const response = await constructorClient.search.getSearchResults('shoes');

constructorClient.tracker.trackSearchResultsLoaded('shoes', {
  resultId: response.result_id, // Include this!
  items: response.results
});

4. Handle Errors Gracefully

Tracking methods return true on success or an Error on failure:
const result = constructorClient.tracker.trackSearchResultClick('shoes', {
  itemId: 'shoe-123'
});

if (result instanceof Error) {
  console.error('Tracking failed:', result.message);
}

5. Monitor Event Queue

Listen for success and error events from the request queue:
constructorClient.tracker.eventemitter.on('success', (data) => {
  console.log('Tracking event sent:', data);
});

constructorClient.tracker.eventemitter.on('error', (data) => {
  console.error('Tracking event failed:', data);
});

Troubleshooting

  1. Verify sendTrackingEvents: true is set during initialization
  2. Check that you’re not in a bot environment (humanity check)
  3. Look for errors in the browser console
  4. Check localStorage for _constructorio_requests key
  1. Check network connectivity
  2. Verify the serviceUrl is correct
  3. Ensure trackingSendDelay hasn’t been set too high
  4. Check if pageUnloading state is blocking sends
The SDK automatically prevents duplicate purchases using orderId. If you see duplicates:
  1. Ensure you’re passing a unique orderId for each order
  2. Check if the order ID storage key _constructorio_purchase_order_ids exists in localStorage

Build docs developers (and LLMs) love