Skip to main content

Discovery OS Shopify headless implementation guide

This guide is for developers building custom frontend components (headless) for Shopify stores that use the Discovery OS Shopify app. Follow it so your UI stays compatible with the app’s automatic tracking and analytics (ATS): impressions, clicks, and session-aware search payloads.

Before you start: Enable the app embed and read route/widget ids from pa-discoveryos-config.


1. HTML structure and attributes

For the app embed to track your products, add the data tags and classes below.

1.1. The wrapper Element (container)

AttributeTypeSource / value
data-pa-route-idstringUse searchRouteId, serpRouteId, or collectionRouteId from recs in pa-discoveryos-config.
data-pa-widget-idstringUse the matching widget id from recs: searchWidgetId, serpWidgetId, or collectionWidgetId from recs in pa-discoveryos-config.
data-pa-search-contextstringContext string for the listing: e.g. the search query ("mountain bike"), or for collections a stable handle / label the app expects (examples below use query terms or collection.handle).

1.2. The product card

Attribute / classTypeDescription
pa-product-cardclassRequired on every product tile the app should track.
data-pa-ref-idattributeProduct reference id (Shopify product id as used by PA).
data-pa-product-indexattribute1-based index in the list (iterator index + 1).

1.3. Example code

1.3.1. Search preview

<div
class="search-preview-wrapper"
data-pa-search-context="{{ search.terms }}"
data-pa-route-id="000-000-000-000" // <searchRouteId>
data-pa-widget-id="aaa-aaa-aaa-aaa" // <searchWidgetId>
>
{% for item in search.results %}
{% if item.object_type == 'product' %}
<div
class="pa-product-card"
data-pa-ref-id="{{ item.id }}"
data-pa-product-index="{{ forloop.index }}"
>
<a href="{{ item.url }}">{{ item.title }}</a>
</div>
{% endif %}
{% endfor %}
</div>

1.3.2. Search results page (SERP) — Liquid

<div
class="search-page-result-wrapper"
data-pa-search-context="{{ search.terms }}"
data-pa-route-id="111-111-111-111" // <serpRouteId>
data-pa-widget-id="bbb-bbb-bbb-bbb" // <serpWidgetId>
>
{% for item in search.results %}
{% if item.object_type == 'product' %}
<div
class="pa-product-card"
data-pa-ref-id="{{ item.id }}"
data-pa-product-index="{{ forloop.index }}"
>
<a href="{{ item.url }}">{{ item.title }}</a>
</div>
{% endif %}
{% endfor %}
</div>

1.3.3. Collection page — Liquid

<div
class="collection-page-result-wrapper"
data-pa-search-context="{{ collection.handle }}"
data-pa-route-id="222-222-222-222" // <collectionRouteId>
data-pa-widget-id="bbb-bbb-bbb-bbb" // <collectionWidgetId>
>
{% for product in collection.products %}
<div
class="pa-product-card"
data-pa-ref-id="{{ product.id }}"
data-pa-product-index="{{ forloop.index }}"
>
<a href="{{ product.url }}">{{ product.title }}</a>
</div>
{% endfor %}
</div>