There are no limits to the number of wigets you can have on a single page, and we'll take care of making sure there are no duplicate items between widgets. There are a few things to keep in mind to ensure your widgets are working together harmoniously however.


Widget Priority

If you are showing multiple widgets on the same page, the priority between the widgets is determined by the order in which you register the widgets. If a particular widget is more important to you (i.e., you want to show better recommendations for it) be sure to register it first. This can have a significant impact on CTR performance - we recommend reading our article on CTR and widget placement to better understand the average performance ranges for common placements. You'll see more improvement on downstream metrics like pages per session and time on site if the highest ranked recommendations are placed in your highest CTR widgets.


Number of Items Per Widget

It is particularly important that you request only as many recommendations per widget as you need to fill that widget. When we deduplicate between widgets, we'll exclude any items that have already been claimed by other widgets. So if a widget that can only display 10 items has requested 30, the bottom 20 items will not be shown elsewhere, and may lead to discarding quality recommendations.


Requesting Any Extra Fields Needed

We allow only one set of fields across all widgets. So, if you need different fields across different widgets (e.g. one widget needs "author" and another needs "description" and "category"), you'll need to request all of those fields globally. That means that all of the items across all widgets will have those extra fields returned, but you don't have to use those extra values in your other widgets if you don't want to. 


Requesting Different Widgets Based On Context

You might have different widget names for different user contexts. A typical scenario is that your site has different layouts for desktop, mobile, and tablet, and you have different sets of widget designs for each one. For instance, you may have both a below-article widget and a right-rail widget in desktop, but only a below-article widget in mobile.


Make sure to only register the widgets that will be displayed in the current context, so that the recommendations shown are the highest ranked. As an example, let's say the user is currently on mobile. If you register your desktop widgets first, then mobile, we will fill in the desktop widgets first with the best recommendations - and the user will never see them! 


Call $p("fetch") only once!

The fetch function must be called exactly once after all widgets have been registered. This way we can make sure there are no duplicate recommendations across widgets, and we can also reduce latency by combining multiple calls together when possible.


Implementing Multiple Widgets - Code Sample

Adding multiple widgets to a page is very similar to adding a single widget, except each one will have it's own set of Template, Recommendation Area, and Widget Code (the Register/Render/Track portion of our SDK) snippets. Below are samples for two widgets together: one called "bottom-widget" and one named "trending-widget".


1. Mustache Templates


<script type="application/mustache"
        id="li-bottom-recommendation-template">
  {{#items}}
  <div class='recommended_item'>
    <a href="{{url}}">
      <img src="{{thumbnail}}" width="150" height="150" />
      <span class="title">{{title}}</span>
    </a>
  </div>
 {{/items}}
</script>

<script type="application/mustache"
        id="li-trending-recommendation-template">
  {{#items}}
  <div class='recommended_item'>
    <a href="{{url}}">
      <img src="{{thumbnail}}" width="150" height="150" />
      <span class="title">{{title}}</span>
    </a> by {{author}}
  </div>
 {{/items}}
</script>



2. Recommendation Areas


<div id="li-bottom-recommendation-unit"></div>
<div id="li-trending-recommendation-unit"></div>



3. Render the Recommendations

// Register calls for different widgets.
$p('register', {
  max: 6, // Number of items you want to show
  widget: 'bottom-widget',
  opts : {},
  callback: function(resp) {
    // Query selector should match div name from Step 2
    var el = document.querySelector('#li-bottom-recommendation-unit');
    // Template should match mustache template name from Step 1
    var template = document.querySelector('#li-bottom-recommendation-template').innerHTML;
    // Basically Mustache.render(template, resp);
    el.innerHTML = $p('render', template, resp);
    // Remove in production
    console.log("Debug was set to true; disable it in production!"); 
    $p('track', {
      elements: document.querySelectorAll('#li-bottom-recommendation-unit > div.recommended_item'), 
      // Match widget name
      name: 'bottom-widget',
      // Source "LI" indicates recommendations are provided
      // by LiftIgniter
      source: 'LI',
      // Optional parameters to track with each event.
      // If the corresponding 'register' call had optional
      // fields, exactly those fields MUST be present here.
      opts: {},
      _debug : true
    });
  }
});

$p('register', {
  max: 4, // Number of items you want to show
  widget: 'trending-widget',
  opts : {},
  callback: function(resp) {
    // Query selector should match div name from Step 2
    var el = document.querySelector('#li-trending-recommendation-unit');
    // Template should match mustache template name from Step 1
    var template = document.querySelector('#li-trending-recommendation-template').innerHTML;
    // Basically Mustache.render(template, resp);
    el.innerHTML = $p('render', template, resp);
    console.log("Debug was set to true; disable it in production!"); // Remove in production
    $p('track', {
      // Div name from Step 1 and recommendation item div name from Step 2
      elements: document.querySelectorAll('#li-trending-recommendation-unit > div.recommended_item'), 
      // Match widget name
      name: 'trending-widget',
      // Source "LI" indicates recommendations are provided
      // by LiftIgniter
      source: 'LI',
      // Optional parameters to track with each event.
      // If the corresponding 'register' call had optional
      // fields, exactly those fields MUST be present here.
      opts: {},
      _debug : true
    });
  }
});

// Fetch recommendations and trigger callbacks for all registered.
$p('fetch');