Skip to content

On-site product recommendations

The Web SDK can show product recommendations on your website: on a product page, in the cart, on a catalog page, or on a 404 page. Products come from the application’s Product catalog. There are two ways to show them:

  • Widget: add an element with the data-pw-recommendations attribute, and the SDK fills it with a grid of products.
  • Headless API: call Pushwoosh.recommendations.fetch and render the products with your own markup.

A live example is the Pushwoosh demo shop at pushon.pushwoosh.com. It has a widget block on the 404 page and headless blocks on the product page and in the cart.

Prerequisites

Anchor link to

Turn on on-site recommendations

Anchor link to

Each application opts in separately, because the feed that serves the blocks is public and doesn’t take an API token. Until then, the feed answers with an error and blocks stay empty.

To turn on on-site recommendations in the Control Panel:

  1. Go to Content → Product Catalog for the application.
  2. Click On-site recommendations.
  3. Turn on Show recommendations on my website.
  4. In Website domains, enter the domains of the websites that will show the blocks, one per line. For example, shop.example.com.
  5. Click Save.
On-site recommendations panel with the Show recommendations on my website switch turned on, one domain in Website domains, and the Save button

After you save:

  • Website domains shows the domains as Pushwoosh stored them: with https:// added, in lowercase, and without duplicates. Check the list for typos.
  • Blocks load only on the listed websites. If you leave Website domains empty, blocks load on any website.
  • The change takes effect within a minute.

If you can’t access the Control Panel, ask Pushwoosh support or your Customer Success Manager to turn on on-site recommendations for the application. Include the application code and the domains of your websites.

Recommendation strategies

Anchor link to
StrategyWhat it showsNeeds
viewedProducts this visitor viewed recently. For a signed-in visitor, products viewed under their user ID come first.Product views sent with trackProductView.
bestsellers_30dThe application’s best-selling products over the last 30 days. Sold-out products are skipped.Order events with the order’s items.
also_boughtProducts that are often bought or viewed together with a given product. Sold-out products are skipped.Product views sent with trackProductView, order events, or any other events that carry a product ID. See Track product activity. The request needs a product ID.

When a strategy has nothing to show, the feed serves the fallback strategy instead. The fallback is bestsellers_30d by default. Set it to none to get an empty answer instead. The answer always tells you which strategy was actually served, so you can change the block’s title.

Track product views

Anchor link to

The viewed and also_bought strategies use product views. On every product page, call trackProductView after the SDK is ready:

Pushwoosh.push(() => {
Pushwoosh.trackProductView({
id: 'SKU-123',
name: 'Linen shirt',
price: 49.9,
currency: 'USD',
category: 'Shirts',
url: 'https://shop.example.com/linen-shirt',
imageUrl: 'https://shop.example.com/img/linen-shirt.jpg',
});
});

Only id is required. Use the same product ID as in your Product catalog. The SDK sends a PW_ProductView event and skips repeat views of the same product within 30 seconds, so a page reload doesn’t count as a second view.

Send order events

Anchor link to

The bestsellers_30d strategy counts only sales from order events, and it’s the default fallback for every block. When a visitor places an order, send PW_OrderCreated:

Pushwoosh.push((api) => {
api.postEvent('PW_OrderCreated', {
orderId: 'ORDER-10293',
items: [
{ productId: 'SKU-123', quantity: 1 },
{ productId: 'SKU-456', quantity: 2 },
],
});
});

orderId is required, because an order without it isn’t counted. In items, use the same product IDs as in your Product catalog. When an order changes, send PW_OrderUpdated with the same orderId and items. To send order events from your server instead, see Bestsellers: add items to your order events.

Add a widget block

Anchor link to

Add an element with the data-pw-recommendations attribute where the block should appear. The SDK loads the widget only on pages that have such an element.

<div
data-pw-recommendations
data-pw-strategy="also_bought"
data-pw-product-id="SKU-123"
data-pw-fallback="bestsellers_30d"
data-pw-limit="4"
data-pw-title="Bought together"
data-pw-fallback-title="Popular right now"
data-pw-block="pdp-also-bought"
data-pw-min-height="260"
></div>
AttributeDescription
data-pw-recommendationsMarks the element as a recommendations block. Required.
data-pw-strategyviewed, bestsellers_30d, or also_bought. Defaults to bestsellers_30d.
data-pw-fallbackStrategy to serve when the main one has nothing: viewed, bestsellers_30d, also_bought, or none. Defaults to bestsellers_30d.
data-pw-limitNumber of products, from 1 to 12. Defaults to 6.
data-pw-product-idThe product on screen. Required for also_bought.
data-pw-excludeComma-separated product IDs not to show, for example the products already in the cart. Up to 50.
data-pw-blockBlock name. It’s added to product links and to click events, so you can tell blocks apart in analytics.
data-pw-titleBlock title.
data-pw-fallback-titleTitle to show when the fallback strategy was served, for example “Popular right now” instead of “You viewed”.
data-pw-min-heightSpace to reserve while the block loads, to avoid a layout shift. A number is read as pixels. Any CSS length also works.

The widget shows each product’s image, name, and price, and links to the product page. Product links carry utm_medium=onsite. A click on a product sends a PW_RecommendationClick event with the block name, the strategy, and the product’s position.

When you change any data-pw-* attribute, the block loads again. For example, update data-pw-product-id when the product on screen changes without a page reload.

Blocks for typical pages

Anchor link to
PageStrategyAttributes to set
Product pagealso_boughtdata-pw-product-id with the product on screen, data-pw-exclude with the same ID
Cartalso_boughtdata-pw-product-id with a product in the cart, data-pw-exclude with all products in the cart
Catalog or home pagebestsellers_30d—
404 pagevieweddata-pw-fallback="bestsellers_30d" and data-pw-fallback-title, so first-time visitors see popular products

Block states

Anchor link to

The widget sets the data-pw-state attribute on the block element:

  • loading: products are loading.
  • ready: products are shown.
  • empty: neither the strategy nor the fallback has products. The block stays empty.
  • error: the products didn’t load. The block stays empty.

Use the state to hide your own frame or heading around an empty block:

.recommendations-frame:has([data-pw-state="empty"], [data-pw-state="error"]) {
display: none;
}

Style the widget

Anchor link to

The widget renders inside a shadow DOM, so your site’s CSS doesn’t reach its content. Set these custom properties on the block element instead:

PropertyDescriptionDefault
--pw-reco-fontFont familyInherited from the page
--pw-reco-accentPrice colorCurrent text color
--pw-reco-radiusCorner radius of product images8px
--pw-reco-columnsNumber of grid columns. On screens up to 600px wide, the grid always has 2 columns.As many as fit
[data-pw-recommendations] {
--pw-reco-columns: 4;
--pw-reco-radius: 0;
--pw-reco-accent: #337ab7;
}

For finer styling, the title, the grid, and each product card are exposed as the title, grid, and card CSS parts, for example [data-pw-recommendations]::part(card).

Single-page applications

Anchor link to

By default, the SDK looks for blocks once, when it’s ready. If your site renders blocks later, for example on navigation in a single-page application, set recommendations.spa to true in the init call:

Pushwoosh.push(['init', {
applicationCode: 'XXXXX-XXXXX',
// other initialization parameters...
recommendations: {
spa: true,
},
}]);

Replace XXXXX-XXXXX with your application code.

The SDK then keeps watching the page and loads the widget once a block appears.

To keep the widget off even when a page has data-pw-recommendations elements, set recommendations.widget to false. The headless API keeps working.

Render blocks with your own markup

Anchor link to

Use the headless API when the widget’s layout doesn’t fit your design. Request products with Pushwoosh.recommendations.fetch and report clicks with Pushwoosh.recommendations.trackClick:

Pushwoosh.push(async () => {
const request = {
strategy: 'also_bought',
productId: 'SKU-123',
exclude: ['SKU-123'],
fallback: 'bestsellers_30d',
limit: 4,
block: 'pdp-also-bought',
};
const { items, strategy } = await Pushwoosh.recommendations.fetch(request);
const title = strategy === request.strategy ? 'Bought together' : 'Popular right now';
renderBlock(title, items, (item, index) => {
Pushwoosh.recommendations.trackClick({
block: request.block,
strategy,
productId: item.id,
position: index + 1,
});
});
});

fetch takes the same parameters as the widget attributes: strategy, fallback, limit, productId, exclude, and block. It returns:

  • items: the products. Each has id, title, description, image_url, url, price, price_formatted, old_price_formatted (only for a product on sale), currency, category, and sku.
  • strategy: the strategy actually served. It differs from the requested one when the fallback was served, and it’s none when the answer is empty.

If the request fails, fetch throws an error. Catch it and hide the block.

trackClick sends the PW_RecommendationClick event. It’s delivered even when the click opens another page. position is the product’s place in the block, starting from 1.

Anchor link to

If communication with Pushwoosh is disabled for the visitor, for example with communicationEnabled: false in the init call (see Manage user consent):

  • trackProductView and trackClick send nothing.
  • Blocks still show products, but the request doesn’t identify the visitor. The viewed strategy has no history to use, so the fallback strategy is served.

Once the visitor enables communication, product views and clicks are tracked again.