Disclaimer: This website requires Please enable JavaScript in your browser settings for the best experience.

Dev GuideAPI Reference
Dev GuideAPI ReferenceUser GuideDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

Widget SDK Reference

Complete reference for configuring and customizing recommendation widgets, including templating, callbacks, and advanced rendering options.

Widget SDK (ip.js)

The ip.js SDK is designed to be flexible, allowing for both simple, static widget placements and advanced, dynamic implementations. The script scans your page for special <script> tags that serve as both configuration and templates for recommendation widgets.

Configuration Methods

You can configure widgets using data-* attributes directly on the <script> tag. This is the simplest and most common method.

Example:

<script
    type="text/x-mustache"
    class="idio-recommendations"
    data-api-key="YOUR_DELIVERY_KEY_HERE"
    data-rpp="5"
    data-section="Case Studies"
    data-fallback="Popular Content"
></script>

Configuration Options

The following data-* attributes are available for widget configuration.

AttributeRequiredDescription
data-api-keyYesYour unique API key for this widget delivery.
data-rppNoResults Per Page. The number of content items to fetch. Overrides the server-side setting.
data-pageNoThe page number of results to fetch, for pagination. Defaults to 1.
data-filterNoA Lucene query to use for personalized recommendations. If data-section is also present, data-filter takes precedence.
data-sectionNoOnly include content from this section. This parameter is ignored if data-filter is provided. If you are using a filter you should specify the section as part of the query.
data-fallbackNoA fallback section name or strategy (e.g., popular or recent) to use if personalization is not possible.
data-fallback-filterNoA Lucene query to use as a fallback. Takes precedence over data-fallback.

Templating

Widgets are rendered using the Mustache templating engine. The ip.js SDK passes an object to your template, with the primary property being content, which is an array of recommended items.

Available Template Variables

Within a {{#content}}...{{/content}} block, you have access to the following variables for each item:

  • {{title}}: The title of the content.
  • {{link_url}}: The destination URL.
  • {{main_image_url}}: The URL for the main image.
  • {{abstract}}: A short summary of the content.
  • {{topics}}: A list of topics associated with the content.
  • {{source}}: The source of the content item.
  • {{author}}: The author of the content item.

Important: To prevent HTML injection, all variables rendered with {{variable}} are HTML-escaped. If you need to render raw HTML, use the triple-mustache: {{{variable}}}. Use this with caution and only with trusted content.

Advanced Usage with Callbacks

For more dynamic control, you can define a global window._ipc object before the ip.js script is loaded. This allows you to define global settings and callbacks.

Example: Global Callbacks

<script type="text/javascript">
var _ipc = {
  complete: function(tag, statusCode) {
    console.log('Success! Widget has been rendered.', tag);
  },
  error: function(tag, statusCode) {
    console.log('There was an error rendering the widget:', statusCode);
  }
};
</script>

Data attributes on an individual widget will always override any settings defined in the global _ipc object.

Advanced Callbacks

For complete control over widget rendering, you can use the finish callback. This callback is invoked when the API response is received, allowing you to implement custom rendering logic, A/B testing, or conditional widget display.

The finish Callback

The finish callback can be defined either in the global _ipc object or in individual widget configurations. It receives four parameters:

finish: function(apiResponse, statusCode, trackLinks, render) {
    // Your custom logic here
}

Parameters:

ParameterTypeDescription
apiResponseObjectThe complete API response containing recommendations and metadata.
statusCodeNumberThe HTTP status code of the API response (e.g., 200, 204, 404).
trackLinksFunctionA helper function to wrap existing links with click tracking.
renderFunctionA helper function to render a Mustache template and mark the recommendation as seen.

API Response Structure

The apiResponse object contains the following key properties:

apiResponse.content (Array) An array of recommended content items. Each item contains:

  • title: Content title
  • link_url: Destination URL
  • main_image_url: Main image URL
  • abstract: Content summary
  • topics: Associated topics
  • source: Content source
  • author: Content author

apiResponse.group (String) The A/B test group assignment for the visitor:

  • 'control': User is in the control group (no personalization)
  • 'test': User is in the test group (personalized content)

Use this to implement different behavior for test vs. control groups.

apiResponse.model (String) Information about the personalization model used for this visitor. Indicates whether recommendations are based on a user interest profile or fallback logic.

apiResponse.recommendation_id (String) A unique identifier for this recommendation decision. This ID is used internally for tracking impressions and clicks.

Callback Helper Functions

trackLinks(arrayOfLinks)

Wraps an array of <a> elements with click tracking URLs. This is useful when you want to track clicks on existing content rather than rendered recommendations.

finish: function(apiResponse, statusCode, trackLinks, render) {
    var existingLinks = document.querySelectorAll('.content-area a');
    trackLinks(existingLinks);
}

render(apiResponse, statusCode, templateString)

Renders a Mustache template with the API response data and marks the recommendation as seen in the analytics system. Returns the rendered HTML string.

finish: function(apiResponse, statusCode, trackLinks, render) {
    var template = '<div>{{#content}}<a href="{{link_url}}">{{title}}</a>{{/content}}</div>';
    var html = render(apiResponse, statusCode, template);
    document.getElementById('widget-container').innerHTML = html;
}

Important: When using the finish callback, recommendations are created with a pending status. Only when you call the render() function will the recommendation be marked as visible and counted in analytics. If you don't call render(), the recommendation will not appear in dashboard metrics.

Use Cases and Examples

Example 1: A/B Testing with Control Groups

Use the apiResponse.group field to show different content to test and control groups:

var _ipc = [{
    api_key: 'YOUR_DELIVERY_KEY',
    section: 'Blog Posts',
    finish: function(apiResponse, statusCode, trackLinks, render) {
        if (apiResponse.group === 'control') {
            // Control group: Track existing static content
            var staticLinks = document.querySelectorAll('#static-content a');
            trackLinks(staticLinks);
            console.log('User in control group - tracking static content');
        } else {
            // Test group: Render personalized widget
            var template = document.getElementById('widget-template').innerHTML;
            var html = render(apiResponse, statusCode, template);
            document.getElementById('personalized-widget').innerHTML = html;
            console.log('User in test group - showing personalized content');
        }
    }
}];

Example 2: Conditional Rendering Based on Content Availability

Only render the widget if sufficient recommendations are available:

var _ipc = [{
    api_key: 'YOUR_DELIVERY_KEY',
    rpp: 5,
    finish: function(apiResponse, statusCode, trackLinks, render) {
        if (statusCode === 200 && apiResponse.content && apiResponse.content.length >= 3) {
            // Enough content available, render the widget
            var template = '{{#content}}<div class="item">{{title}}</div>{{/content}}';
            var html = render(apiResponse, statusCode, template);
            document.getElementById('recommendations').innerHTML = html;
        } else {
            // Not enough content, hide the widget container
            document.getElementById('recommendations').style.display = 'none';
        }
    }
}];

Example 3: Custom Template Selection Based on Personalization

Use different templates depending on whether content is personalized:

var _ipc = [{
    api_key: 'YOUR_DELIVERY_KEY',
    finish: function(apiResponse, statusCode, trackLinks, render) {
        var template;

        if (apiResponse.model && apiResponse.model.includes('personalized')) {
            // Use premium template for personalized content
            template = document.getElementById('personalized-template').innerHTML;
        } else {
            // Use basic template for non-personalized content
            template = document.getElementById('basic-template').innerHTML;
        }

        var html = render(apiResponse, statusCode, template);
        document.getElementById('widget-area').innerHTML = html;
    }
}];

Example 4: Programmatic Widget Loading

Dynamically load widgets based on user interaction:

function loadRecommendations(section, containerId) {
    var config = {
        api_key: 'YOUR_DELIVERY_KEY',
        section: section,
        finish: function(apiResponse, statusCode, trackLinks, render) {
            if (statusCode === 200) {
                var template = '{{#content}}<a href="{{link_url}}">{{title}}</a>{{/content}}';
                var html = render(apiResponse, statusCode, template);
                document.getElementById(containerId).innerHTML = html;
            }
        }
    };

    // Use the idio.load() API to load recommendations dynamically
    idio.load([config]);
}

// Call when user clicks a button
document.getElementById('load-more').addEventListener('click', function() {
    loadRecommendations('Related Articles', 'dynamic-widget-1');
});