Skip to content

Personalized Product Recommendations on CMS Pages

⏱ 60 minutes advanced
📜AdvancedcmsODPcommerce

A visitor browses your website — viewing product pages, reading articles, comparing features. They leave without purchasing. When they return the next day, your site shows the same generic content to everyone. Their browsing history, interests, and intent are lost.

This recipe connects three Optimizely products to solve this: ODP captures and unifies visitor behavior, Recommendations uses that data to compute relevant suggestions, and CMS renders those recommendations on the right pages at the right time.

┌──────────────┐ Events ┌──────────────┐
│ Website │ ──────────────→ │ ODP │
│ (CMS pages) │ │ (profiles) │
└──────┬───────┘ └──────┬───────┘
│ │
│ Page request │ Behavioral data
│ ▼
│ ┌──────────────┐
│ │ Recs API │
│ │ (models) │
│ └──────┬───────┘
│ │
│ ◄─────── Recommendations ─────┘
┌──────────────┐
│ CMS Block │
│ (renders │
│ recs widget)│
└──────────────┘

Data flow:

  1. Visitor browses CMS pages → ODP tracks page views, product views, and interactions
  2. ODP builds a unified customer profile with behavioral history
  3. When a CMS page loads, the recommendations widget queries the Recs API
  4. The Recs API uses the visitor’s ODP profile to compute personalized recommendations
  5. The CMS block renders the recommended products/content

Step 1: Ensure ODP event tracking is active

Section titled “Step 1: Ensure ODP event tracking is active”

Product recommendations require behavioral data. Verify that your site is tracking the right events.

Required events:

EventWhen to fireWhat it captures
pageviewEvery page loadURL, title, content type
productProduct page viewProduct ID, name, category, price
add_to_cartCart additionProduct ID, quantity
purchaseCompleted orderOrder ID, products, total
Track product view events
javascript
// Fire on product page load
zaius.event('product', {
  product_id: 'SKU-12345',
  name: 'Wireless Headphones',
  category: 'Electronics > Audio',
  price: 79.99,
  url: window.location.href,
});

If you have not set up event tracking yet, follow the Set Up Event Tracking guide first.

Recommendation models define the algorithm that selects products for each visitor.

Common model types:

ModelAlgorithmBest for
Recently viewedShows items the visitor viewedProduct detail pages
Frequently bought togetherCo-purchase analysisCart page, product page
Similar itemsContent/attribute similarityCategory pages
TrendingPopular items by recent activityHomepage, landing pages
PersonalizedML model using full behavioral profileAny page

Configure your model in the Optimizely Recommendations dashboard:

  1. Navigate to Recommendations → Models
  2. Click “Create Model”
  3. Select the algorithm type
  4. Set the data source (which ODP events feed the model)
  5. Configure filters (category, price range, availability)
  6. Activate the model

Step 3: Create a CMS block for recommendations

Section titled “Step 3: Create a CMS block for recommendations”

Build a CMS block type that renders the recommendation widget.

Recommendations block type
csharp
using EPiServer.Core;
using EPiServer.DataAnnotations;
using System.ComponentModel.DataAnnotations;

[ContentType(
    DisplayName = "Product Recommendations",
    GUID = "f1e2d3c4-b5a6-7890-cdef-012345678901",
    Description = "Displays personalized product recommendations")]
public class RecommendationsBlock : BlockData
{
    [Display(Name = "Headline", Order = 10)]
    public virtual string Headline { get; set; }

    [Display(Name = "Model ID", Order = 20)]
    [Required]
    public virtual string ModelId { get; set; }

    [Display(Name = "Max Items", Order = 30)]
    public virtual int MaxItems { get; set; } = 4;

    [Display(Name = "Fallback Content", Order = 40)]
    public virtual ContentArea FallbackContent { get; set; }
}

The FallbackContent property is important — if the Recommendations API returns no results (new visitor with no history), the block shows fallback content instead of an empty space.

Step 4: Render recommendations client-side

Section titled “Step 4: Render recommendations client-side”

The recommendations widget loads asynchronously to avoid blocking page render.

Client-side recommendations widget
javascript
async function loadRecommendations(modelId, maxItems, containerId) {
  const visitorId = zaius.getVisitorId();

  const response = await fetch(
    `https://api.zaius.com/v3/recommendations/${modelId}`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': RECS_API_KEY,
      },
      body: JSON.stringify({
        visitor_id: visitorId,
        max_results: maxItems,
      }),
    }
  );

  const { recommendations } = await response.json();
  const container = document.getElementById(containerId);

  if (recommendations.length === 0) {
    // Show fallback content
    container.querySelector('.recs-fallback')?.classList.remove('hidden');
    return;
  }

  // Render product cards
  container.innerHTML = recommendations
    .map(product => `
      <a href="${product.url}" class="recs-card">
        <img src="${product.image_url}" alt="${product.name}" />
        <h4>${product.name}</h4>
        <span class="price">$${product.price}</span>
      </a>
    `).join('');
}
  1. Open a CMS page in the editor
  2. Find a ContentArea (e.g., “Below Content” or “Sidebar”)
  3. Add a Product Recommendations block
  4. Set the Headline (e.g., “Recommended for You”)
  5. Enter the Model ID from Step 2
  6. Set Max Items (4–8 is typical)
  7. Optionally add Fallback Content (a promotional block or featured products)
  8. Publish the page

Step 6: Personalize recommendations with visitor groups

Section titled “Step 6: Personalize recommendations with visitor groups”

Combine recommendations with CMS visitor groups for layered personalization:

  • New visitors → Show “Trending” recommendations (no personal history yet)
  • Returning visitors → Show “Personalized” recommendations (use their browsing history)
  • High-value customers → Show premium products with exclusive offers

Set this up by creating a ContentArea with personalized blocks — each visitor group gets a different RecommendationsBlock pointing to a different model.

Track recommendation performance to validate the approach:

MetricWhat to measureHow
Click-through rate% of visitors who click a recommendationTrack recs_click event in ODP
Conversion rate% of recs clicks that lead to purchaseODP attribution
Revenue per impressionAverage revenue generated per recs widget viewODP computed metric
Coverage% of visitors receiving personalized (not fallback) recsRecs API analytics

Consider A/B testing the recommendations widget itself — use Web Experimentation to test whether recommendations increase conversion vs. the page without them.