Query pagination

Sometimes, you might want to show recommendations powered by LiftIgniter across multiple pages, or on a single page with an infinite scroll of recommendations, such as in a feed.

Use cases

The following are some of the use cases we will cover here.

  • Infinite scroll of recommendations, typically seen for a feed: Here, new batches of recommendations load once the user has scrolled through the existing batch of recommendations. Note that this is different from an infinite scroll of full articles, which is described in the Single Page Application documentation.
  • A paginated set of recommendations, where the user can jump to a specific page by clicking on the page number (single-page implementation, so that all the "pages" are part of the same DOM).
  • A paginated set of recommendations, where the user can jump to a specific page by clicking on the page number (each page corresponds to a separate HTML page).


When powering a paginated set of results using a LiftIgniter query, we need to make sure of the following:

  • Proper tracking: Data sent to LiftIgniter correctly records what page of recommendations was shown, and only includes items visible on that page.
  • Consistency (applicable in some contexts): If the user has already visited a page and then revisits that page, the same set of results is shown.
  • Deduplication: The different pages should have non-overlapping sets of results.

Proper tracking

You need to call our tracking function for each "page" of recommendations.

When tracking LiftIgniter recommendations, there are two main changes you should make when dealing with paginated recommendations.

  • If your different recommendation pages are part of the same DOM (as would occur in the case of an infinite scroll of recommendations) you need to make sure that the selector you use to define your elements when tracking only matches the recommendations in that page.
  • In order for our system to correctly identify what numbered page it is, you should include opts: {page: "page number"} in your $p("track",...) function.

Here's what your tracking call will look like (with changes indicated):

$p('track', {
    opts: {page: 2}, // or whatever your page number is. This helps us separately track the different pages despite them having the same widget name
    elements: document.querySelectorAll('selector that matches ONLY the recommendations on that page'), // Be careful about colliding selectors in the case that all the recommendation pages are in the same DOM, e.g., in an infinite scroll or feed of recommendations
    name: 'your widget name', // widget name can be the same across the different pages
    source: 'LI' // or 'base' if it's the base slice


In some cases, you care about consistency for paginated recommendations: if a user returns to a page the user has already visited, then the user should see the same recommendations that he or she already saw.

For an infinite scroll or single-page app, where you are storing the already-visited recommendations in the DOM, consistency is automatic.

In other cases, you would need to store the recommendations rendered on already-visited pages in the client's local storage. If the end user revisits a page, then the recommendations get pulled from local storage, thereby guaranteeing consistency.


In most cases, you'd want to make sure that the recommendations shown on different pages are different from each other.

There are two ways of doing the deduplication:

  • Implicit deduplication (handled by LiftIgniter): LiftIgniter automatically deduplicates recommendations across pages using a pageviewId field that is automatically packaged in by our JavaScipt SDK. However, this deduplication happens only between recommendations returned by LiftIgniter. We must get a widget_shown event in order to deduplicate. It may not work correctly if:
    the user navigates between multiple URLs of the site and then returns to an earlier URL (as we keep server-side records for only one current page/URL). If you are doing an API integration, you must explicitly include a value for the pageviewId across all your widget requests so that we know each request is for the same current page and that the user has not moved off the current URL.
    A widget_shown event is not received - our models will interpret this as though the items were not loaded on the page, and are therefore still valid recommendations
  • Explicit deduplication: You can send excludeItems in your query with a list of the items that have already been shown and that you wish to exclude.