Marking Rendered Recommendations
When you render recommendation blocks yourself — instead of using Perselio-managed widgets — the Perselio analytics collector has no way to tell that the displayed items came from a Perselio recommendation. A lightweight DOM contract solves this: mark the rendered block with data attributes so impressions and interactions can be attributed to Perselio recommendations.
Use this contract whenever you call a Perselio recommendation API and render the resulting UI on your own pages. It applies to both integration styles that render their own UI:
- Browser SDK — you fetch and render in the browser.
- Backend to API — your backend fetches and your templates render the markup server-side.
Without these attributes, recommendation performance (CTR, contribution to revenue) cannot be reported.
HTML attributes
Add the attributes to the element that wraps the recommended items:
<div
data-per-recommender="homepage-carousel"
data-per-recommendation-id="abc12345-0000-0000-0000-000000000000"
>
<!-- recommended products rendered by you -->
</div>data-per-recommender— required. Marks a DOM area as a customer-rendered Perselio recommendation surface. The value is a display name describing the placement, for examplehomepage-carousel,product-detail-similar, orcart-upsell. It is used to label the surface in analytics dashboards.data-per-recommendation-id— optional but recommended. When present, it must contain therecommendationIdreturned by the recommendation API response. This links the rendered impression to the exact API response.
Passing the recommendation ID from the API response
The recommendation API returns a recommendationId with every response. Carry it into the rendered markup.
Server-rendered (backend integration):
<div
data-per-recommender="product-detail-similar"
data-per-recommendation-id="{{ recommendation.recommendationId }}"
>
{% for item in recommendation.items %}
<!-- your product card -->
{% endfor %}
</div>Browser-rendered (JavaScript SDK):
window.perselioReady(async function (perselio) {
const result = await perselio.client.recommendation.getRecommendation({
scenario: { scenarioId: 'recommended_for_you' },
includeFields: ['url', 'itemId', 'name'],
limit: 4,
});
const container = document.querySelector('#homepage-recos');
container.setAttribute('data-per-recommender', 'homepage-carousel');
container.setAttribute('data-per-recommendation-id', result.recommendationId);
// render result.items inside `container` …
});What Perselio tracks from marked blocks
- Impressions — when a marked recommendation surface becomes visible.
- Interactions — clicks and other interactions on items inside a marked surface.
If data-per-recommendation-id is present, analytics is linked to the exact recommendation API response. If it is missing, events are still classified as Perselio recommendation interactions, but with weaker attribution.
Identifying the items inside the block
The wrapper attributes say this block is a Perselio recommendation. They do not say which items it contains — that comes from the item identifier on each card, the same convention used everywhere else on your site. See Identifying Products on Your Site.
<div
data-per-recommender="homepage-carousel"
data-per-recommendation-id="abc12345-0000-0000-0000-000000000000"
>
<div class="product-card" data-per-item-id="12345">…</div>
<div class="product-card" data-per-item-id="67890">…</div>
</div>The IDs must appear verbatim in the Item Feed, exactly as on the rest of the site. If your templates already expose an identifier under a different attribute, tell us — the collector is configured per site and can read it instead.
No recommendation-specific item markup is needed: no per-position attributes, no extra IDs. The cards carry the same identifier they would carry in a category listing.
Prerequisites
The Data Collector script must be present on the page. It is what observes the marked blocks and sends the events — it is required even when every API call is made from your backend.
Validation checklist
- The wrapper element carries
data-per-recommenderwith a descriptive value. data-per-recommendation-idis set from the API responserecommendationId(recommended).- The attributes are present on the element that wraps the recommended items, not on the individual items.
- Each rendered card carries its item identifier, as on any other listing — see Identifying Products on Your Site.
- The recommendation surface is marked before it becomes visible to the user.
Related
- Identifying Products on Your Site — the identifiers the cards inside the block must carry
- Data Collector — the script that observes marked blocks and sends the events
- Marking Rendered Search Results — the same contract for autocomplete and search results
Updated about 13 hours ago

