Widget SDK Reference
Complete reference for configuring and customizing recommendation widgets, including templating, callbacks, and advanced rendering options.
Widget SDK (ip.js)
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.
| Attribute | Required | Description |
|---|---|---|
data-api-key | Yes | Your unique API key for this widget delivery. |
data-rpp | No | Results Per Page. The number of content items to fetch. Overrides the server-side setting. |
data-page | No | The page number of results to fetch, for pagination. Defaults to 1. |
data-filter | No | A Lucene query to use for personalized recommendations. If data-section is also present, data-filter takes precedence. |
data-section | No | Only 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-fallback | No | A fallback section name or strategy (e.g., popular or recent) to use if personalization is not possible. |
data-fallback-filter | No | A 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
finish CallbackThe 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:
| Parameter | Type | Description |
|---|---|---|
apiResponse | Object | The complete API response containing recommendations and metadata. |
statusCode | Number | The HTTP status code of the API response (e.g., 200, 204, 404). |
trackLinks | Function | A helper function to wrap existing links with click tracking. |
render | Function | A 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 titlelink_url: Destination URLmain_image_url: Main image URLabstract: Content summarytopics: Associated topicssource: Content sourceauthor: 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
finishcallback, recommendations are created with apendingstatus. Only when you call therender()function will the recommendation be marked as visible and counted in analytics. If you don't callrender(), 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');
});Updated 3 days ago