We'll cover the essential steps that you need to follow to build a Recommendations experience for your site.
Let's start with a quick overview that applies to all of Qubit's experiences.
Each experience consists of the same basic anatomy:
Once Qubit's script loads on the page, each experience goes through the following lifecycle:
triggers.js
is executedvariation.js
is executed and variation.css
is injected into the pagefields.json
to allow non-technical team members to easily configure the experience once builtpackage.json
to declare the Qubit recommendations package dependenciestriggers.js
to make sure there are recommendations to show and ensure the element we wish to append the recommendations to existsvariation.js
to render the recommendations out and ensure that the required events that power the metrics in the Qubit dashboard are emittedvariation.css
to render the recommendations carousel correctlySelect Experiences from the side menu, select New experience, Custom, then Choose
Enter a name for the experience and a description, if required, and then select Create
Because we want to enable non-technical team members to be able to configure the experience once built, we always look to create a fields.json specification.
If you set your experience up as a template, defining a fields.json specification is a great way to allow non-technical team members to add the recommendations carousel where it's needed and retain a degree of control over how the experience behaves on site.
In this example, we're going to add a specification to enable non-technical team members to set several options:
You can extend or reduce these options at leisure depending on how complex your recommendations setup and design are.
Let's take a look at what we are aiming for, before discussing how to achieve this:
Select fields.json from the side menu and add the following lines:
{
"fields": [
{
"key": "page",
"type": "String",
"label": "Page",
"groupId": "settings",
"footnote": "which page the recommendations will show on",
"required": true,
"constraints": {
"values": [
{
"label": "Product",
"value": "product"
},
{
"label": "Basket",
"value": "basket"
}
]
}
},
{
"key": "title",
"type": "String",
"label": "Title",
"groupId": "settings",
"footnote": "e.g. You may also like..",
"required": true
},
{
"key": "limit",
"type": "Number",
"label": "Maximum number of recommendations",
"groupId": "settings",
"footnote": "the maximum amount of products to show in the carousel",
"required": true
},
{
"key": "slidesToShow",
"type": "Number",
"label": "Number of recommendations to display",
"groupId": "settings",
"footnote": "how many products are visible before scrolling",
"required": true
},
{
"key": "slidesToScroll",
"type": "Number",
"label": "Number of recommendations to slide upon scroll",
"groupId": "settings",
"footnote": "how many new products are shown when the user scrolls",
"required": true
},
{
"key": "infinite",
"type": "Boolean",
"label": "Infinite scroll",
"groupId": "settings",
"footnote": "Should the carousel scroll infinitely?",
"required": true
},
{
"key": "views",
"type": "String",
"label": "Social proof copy",
"groupId": "settings",
"footnote": "where {{count}} to be replaced by view count",
"required": true
},
{
"key": "element",
"type": "String",
"label": "Element to insert after",
"groupId": "settings",
"footnote": "e.g. #product .reviews",
"required": true
}
],
"groups": [
{
"id": "settings",
"title": "Recommendations settings"
}
]
}
In the package.json file, we declare our package dependencies for the experience.
Select packages.json from the side menu and add the following lines:
{
"dependencies": {
"slapdash": "^1.3.3",
"@qubit/recommendations": "^1.1.3"
}
}
For more information about packages, see Packages & Code Re-use.
In the triggers.js file, we must fulfill 2 requirements. Firstly, we must ensure we have some recommendations to show. Secondly, we must ensure the element we wish to append the recommendations to exists before we show recommendations.
Without making these checks, we risk firing the experience and counting a user as having seen the recommendations when really none were available and/or attempting to append the recommendations to an element that no longer exists on site or hasn't yet loaded.
See Polling For Elements for more information about how to use Qubit's poller module.
We initialize the @qubit/recommendations
package as described in Standard example.
Select triggers.js from the side menu, and add the following lines:
module.exports = function triggers (options, cb) {
const $ = window.jQuery
const { data, log, state } = options
const { page, element } = data
const strategy = page === 'product' ? 'pp1' : 'pp3'
const recommendations = require('@qubit/recommendations')(options, { strategy })
const slickUrl = '//d1m54pdnjzjnhe.cloudfront.net/js-libs/slick/slick_requirable.1.6.min.js'
const isProduct = /\/products\//i.test(window.location.pathname)
if (isProduct) {
options.uv.on('ecProduct', (e) => {
const { productId } = e.product
if (productId) {
recommendations.get({ seed: productId })
.then(poll)
.catch(log.info)
}
}).replay()
}
function poll (recsItems) {
options.poll(element).then(el => {
require([slickUrl], (slick) => {
state.set('state', { recommendations, recsItems, slick, $el: $(el) })
cb(true)
})
})
}
}
This example assumes a Product page setup where all product pages contain /products/
in the URL. It is always wise to guard your trigger code so that it only runs where it is required.
Following the URL check, we listen for the ecProduct QProtocol event and extract the product Id so we can provide this context as the seed to the Recommendations API.
If you do not have a suitable QProtocol implementation, your seed will need to be retrieved from elsewhere, perhaps an element on the page or an alternative data layer. See Building The Catalog for more information.
WARNING: Failing to provide a seed will result in only popular items being shown.
In basket page/multiple item scenarios, all products Ids in the basket should be provided as an Array of Strings, e.g. ['a1', 'b2', 'c3']
.
Our variation code should be relatively straightforward - at this point our triggers should have verified we have recommendations to display and that an element exists to append them to.
So now, we simply need to render the recommendations out and ensure that events that power the recommendations reporting and metrics in the Qubit dashboard are emitted.
These events are shown in the following example where:
recommendations.shown
is called for every recommendation rendered onto the pagerecommendations.clicked
is bound to each recommendation to track every click."We also use some of the configuration made available to us through our fields.json specification. This should mean that very little code changes will be required in future.
Should you wish to extend or reduce the implementation, for example, removing the social proof views, these can simply be removed from the HTML markup and the associated field be removed from fields.json.
Select variation.js from the side menu and add the following lines:
module.exports = function variation ({ data, state }) {
const _ = require('slapdash')
const $ = window.jQuery
const { infinite, slidesToShow, slidesToScroll, title, views } = data
const { recommendations, recsItems, $el, slick } = state.get('state')
slick($)
const $items = recsItems.map((item, i) => {
const { details } = item
const onSale = details.unit_price !== details.unit_sale_price
recommendations.shown(item)
return $(`
<div class="qubit-recommendation qubit-recommendation-onSale-${onSale}">
<a class="qubit-recommendation-link" href="${details.url}">
<img class="qubit-recommendation-image" src="${details.image_url}" alt="${details.name}" />
<div class="qubit-recommendation-name">${details.name}</div>
<div class="qubit-recommendation-wasPrice">${formatPrice(details.unit_price)}</div>
<div class="qubit-recommendation-nowPrice">${formatPrice(details.unit_sale_price)}</div>
<div class="qubit-recommendation-views">${views.replace('{{count}}', details.views)}</div>
</a>
</div>
`).click(() => {
recommendations.clicked(_.assign({ position: i + 1 }, item))
})
})
const $html = $(`
<div class="qubit-recommendations">
<div class="qubit-recommendations-title">${title}</div>
<div class="qubit-recommendations-carousel"></div>
</div>
`)
const $carousel = $html.find('.qubit-recommendations-carousel')
$carousel.append($items)
$el.after($html)
$carousel.slick({ infinite, slidesToShow, slidesToScroll })
function formatPrice (price) {
return '£' + price.toLocaleString('en-GB', { minimumFractionDigits: 2 })
}
}
If you have existing carousel functionality on-site, this can also be reused rather than pulling down the Slick library shown in our example.
As we are using the Slick carousel package, a section of CSS is required for this to render correctly. Additionally, we have included some basic styles to help the content inside the carousel to render more appropriately.
By giving all of our elements appropriate class names, this CSS is easily extensible for adding your own branding in very little time.
Select Variation.css from the side menu and add the following lines:
// wrapping element
.qubit-recommendations {
}
.qubit-recommendations-title {
text-align: center;
font-size: 20px;
padding: 10px;
}
// each recommendation block
.qubit-recommendation {
}
.qubit-recommendation-link {
text-decoration: none;
}
.qubit-recommendation-image {
width: 100%;
}
.qubit-recommendation-name {
}
// both prices on or off sale
.qubit-recommendation-wasPrice, .qubit-recommendation-nowPrice {
display: inline-block;
}
.qubit-recommendation-wasPrice {
display: none;
text-decoration: line-through;
}
.qubit-recommendation-nowPrice {
}
// recommendation on sale
.qubit-recommendation-onSale-true {
.qubit-recommendation-wasPrice {
display: inline-block;
}
.qubit-recommendation-nowPrice {
color: #f42145;
}
}
// recommendation not on sale
.qubit-recommendation-onSale-false {
}
/* carousel */
.qubit-recommendations-carousel {
&.slick-slider {
position: relative;
display: block;
box-sizing: border-box;
user-select: none;
touch-action: pan-y;
-webkit-tap-highlight-color: transparent;
}
.slick-list {
position: relative;
overflow: hidden;
display: block;
margin: 0 -10px;
padding: 0;
&:focus {
outline: none;
}
&.dragging {
cursor: pointer;
cursor: hand;
}
}
&.slick-slider .slick-track,
&.slick-slider .slick-list {
transform: translate3d(0, 0, 0);
}
.slick-track {
position: relative;
left: 0;
top: 0;
display: block;
&:before,
&:after {
content: "";
display: table;
}
&:after {
clear: both;
}
}
.slick-loading.slick-track {
visibility: hidden;
}
.slick-slide {
float: left;
height: 100%;
min-height: 1px;
display: none;
margin: 0 10px;
img {
display: block;
}
&.slick-loading img {
display: none;
}
&.dragging img {
pointer-events: none;
}
}
[dir="rtl"] .slick-slide {
float: right;
}
&.slick-initialized .slick-slide {
display: block;
}
.slick-loading .slick-slide {
visibility: hidden;
}
.slick-vertical .slick-slide {
display: block;
height: auto;
border: 1px solid transparent;
}
.slick-arrow {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: transparent;
cursor: pointer;
border: 0;
z-index: 1;
}
.slick-arrow.slick-hidden {
display: none;
}
.slick-prev,
.slick-next {
font-size: 0;
}
.slick-prev:before,
.slick-next:before {
font-size: 20px;
color: #000;
opacity: 0.75;
}
.slick-prev:before {
content: "\2190";
}
.slick-next:before {
content: "\2192";
}
.slick-prev {
left: 0px;
}
.slick-next {
right: 0px;
}
}
We are now at a point where we can preview the experience. To do this, select .
Here's the result:
Once the experience has been completed, the next step will be to configure it. This can also be completed by non-technical members of your team and involves adding goals, deciding which segments to target with the experience, and what percentage of your traffic to allocate the experience to. You might also decide to schedule the experience. All this and more can be done in the Settings tab. See Configuring Recommendations Experiences for full details.
With the basic setup and configuration done, the final step will be to publish the experience. You can do this by selecting Publish experience. Before doing this, we recommend that you preview it on site to ensure that it fires correctly according to the designed triggers, displays correctly, and crucially that it functions according to your expectations. See Going Live With Your Experience.
When you publish an experience it will be pushed live onto your site or mobile platform. Visitors will be served the experience, of course depending on the conditions established for the experience, including triggers, segmentation, and traffic allocation.
Once an experience is live it will start to generate data, which you will use to interpret the success of the experience against its defined goals. See for details.