Build Atomic commerce interfaces with the Coveo app for Shopify
Build Atomic commerce interfaces with the Coveo app for Shopify
This page explains how to integrate the Coveo Atomic search box, search page, product listing page, and recommendation components in your Shopify store. The details vary depending on the theme you use, but the core logic remains the same.
This article uses the default Dawn theme as its starting point.
|
This article assumes that you’re using the Coveo AI Search & Discovery app, which allows for certain simplifications. If that’s not the case, see Build Atomic commerce interfaces in a Shopify store without the Coveo AI Search & Discovery app. |
Load Atomic and build the Commerce engine
-
In your Shopify theme editor, create a snippet named
atomic.liquid
and paste the following code. For optimization, this snippet relies on promises.<!-- snippets/atomic.liquid --> <script type="module"> window.loadAtomic = function () { if (document.getElementById('atomic-script')) {
return customElements.whenDefined('atomic-commerce-interface');
} var script = document.createElement('script'); script.src = 'https://static.cloud.coveo.com/atomic/v3/atomic.esm.js';
script.type = 'module'; script.setAttribute('id', 'atomic-script'); document.head.appendChild(script); var css = document.createElement('link'); css.rel = 'stylesheet'; css.href = 'https://static.cloud.coveo.com/atomic/v3/themes/coveo.css';
document.head.appendChild(css); return customElements.whenDefined('atomic-commerce-interface'); }; </script>
Checks whether the Atomic scripts have already been loaded. Returns a promise that resolves when the atomic-commerce-interface
custom element is defined.The Atomic Commerce JavaScript module. The Atomic Commerce CSS file. -
Create a snippet named
engine.liquid
in which you paste the following code.<!-- snippets/engine.liquid --> <script type="module"> window.configureHeadless = async (config) => { const {buildShopifyCommerceEngine} = await import('https://static.cloud.coveo.com/shopify/v1/headless/commerce.esm.js'); if (window.CoveoEngine) { if (!window.CoveoCartController) { window.CoveoCartController = window.CoveoEngine.buildCart();
} return window.CoveoEngine; } window.CoveoEngine = buildShopifyCommerceEngine({ commerceEngineOptions: { configuration: {
accessToken: config.accessToken, organizationId: config.organizationId, analytics: { enabled: true, trackingId: config.trackingId, }, context: {
country: {{ localization.country.iso_code | json }}, currency: {{ localization.country.currency.iso_code | json }}, view: { url: {{ canonical_url | json }}, }, language: {{ request.locale.iso_code | json }}, cart: {{ cart.items | json }}.map(function (item) { return { productId: `gid://shopify/ProductVariant/${item.variant_id}`, name: item.title, price: item.final_price, quantity: item.quantity, }; }), }, }, }, }); window.CoveoCartController = window.CoveoEngine.buildCart(); return window.CoveoEngine; }; </script>
Create a cart controller instance from the initialized Atomic engine and assigns it to window.CoveoCartController
. Essential for keeping analytics and recommendations accurate when using the Shopify AJAX API, see Keep Atomic Cart State in sync with Shopify (AJAX use case) for details.Retrieve the access token, organization ID and tracking ID from a config
object you’ll pass in the next steps.The context
object containing the country, currency, view, language, and cart information, extracted from the Shopify store.
You’ll need to include this snippet in every page of your Shopify store in which you want to use Coveo Atomic Commerce components.
|
Note for non-AJAX users
If your storefront uses form based cart submissions, each page reload automatically re-initializes the Atomic engine with the updated cart state. No additional syncing is required in that case. |
Keep Atomic Cart State in sync with Shopify (AJAX use case)
When using Shopify AJAX APIs to update the cart, update the Atomic cart controller to keep the search and analytics state in sync.
|
Note
Form based cart submissions which reload the page cause the Atomic engine to re-initialize with the updated cart state, so no additional work is required. |
Sync the Atomic Cart Controller after AJAX cart updates
After any AJAX cart update, synchronize the Atomic cart controller with the latest Shopify cart state.
The following example demonstrates how to add an item to the Shopify cart using the /cart/add.js
endpoint, then fetch the updated cart state to be synced with the cart controller.
Be sure to implement a similar routine after every AJAX cart change event in your storefront.
For more details on the AJAX cart API, see the Shopify cart API reference.
fetch('/cart/add.js', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({id: VARIANT_ID, quantity: 1})
})
.then(response => response.json())
.then(() => {
fetch('/cart.js')
.then(response => response.json())
.then(cart => {
const atomicCartItems = cart.items.map(item => ({
productId: `gid://shopify/ProductVariant/${item.variant_id}`,
name: item.title,
price: item.final_price,
quantity: item.quantity,
}));
window.CoveoCartController.setItems(atomicCartItems);
});
});
|
Analytics and recommendations may become out of sync if they’re not updated after an AJAX cart change. |
Initialize the web pixel
The Coveo Atomic library only logs click events, so the Coveo AI Search & Discovery app uses a web pixel to log cart events, product view events, and purchase events.
The web pixel must be initialized before it can log events to power your Coveo ML models. To initialize it, create an initialization snippet as follows and add it to every page of your Shopify store.
-
Create a snippet named
initialize-analytics.liquid
and paste the following code:<!-- snippets/initialize-analytics.liquid --> <script type="module"> import {init, fetchAppProxyConfig} from 'https://static.cloud.coveo.com/shopify/v1/headless/commerce.esm.js'; const config = await fetchAppProxyConfig({
marketId: {{ localization.market.id | encode_url_component }}, }); init(config);
</script>
The Coveo Shopify app contains an App Proxy endpoint that returns an object containing the access token, organization ID and tracking ID, which are required to initialize the web pixel. The init
function initializes the web pixel with the configuration returned by the App Proxy endpoint (see @coveo/shopify). -
Include this snippet in your
theme.liquid
file, just before the closing</head>
tag:{% render 'initialize-analytics' %}
-
Verify your setup by loading a storefront page and confirming that cart events, product view events, and purchase events appear in your analytics.
Search page
To implement a Coveo Atomic search page or replace an existing search page with an Atomic one, do the following:
-
Create a snippet named
initialize-search-page.liquid
to initialize the Atomic interface.<!-- snippets/initialize-search-page.liquid --> <script type="module"> const [response] = await Promise.all([ fetch('/apps/coveo?marketId={{ localization.market.id | encode_url_component }}'),
window.loadAtomic(), ]); const config = await response.json(); const engine = await window.configureHeadless(config); const searchPage = document.querySelector('#search-page'); await searchPage.initializeWithEngine(engine);
searchPage.executeFirstRequest();
</script>
The Coveo Shopify app contains an App Proxy endpoint that returns an object containing the access token, organization ID, and tracking ID, which are required to build the Commerce engine. Initialize the search page with the Coveo engine. The executeFirstRequest
method sends the first query to the Coveo engine, ensuring the search page is populated with results when it loads. -
Open your theme and locate the code that renders the search results page.
NoteIn the Dawn theme, this code is located in
sections/main-search.liquid
. -
Remove the existing search page code from this file, if any exists.
-
Add the initialization snippets and an
atomic-commerce-interface
element of typesearch
that includes the target Atomic search page components, as follows:<!--- sections/main-search.liquid ---> {% render 'engine' %} {% render 'atomic' %} {% render 'initialize-search-page' %} <atomic-commerce-interface id="search-page" type="search"> <atomic-commerce-layout> <atomic-layout-section section="search"> <atomic-commerce-search-box> <atomic-commerce-search-box-recent-queries></atomic-commerce-search-box-recent-queries> <atomic-commerce-search-box-query-suggestions></atomic-commerce-search-box-query-suggestions> <atomic-commerce-search-box-instant-products image-size="small"> <atomic-product-template> <template> <atomic-product-section-name> <atomic-product-link></atomic-product-link> </atomic-product-section-name> <atomic-product-section-visual> <atomic-product-field-condition if-defined="ec_thumbnails">
<atomic-product-image field="ec_thumbnails"></atomic-product-image> </atomic-product-field-condition> </atomic-product-section-visual> <atomic-product-section-metadata> <atomic-product-field-condition if-defined="ec_brand"> <atomic-product-text field="ec_brand"></atomic-product-text> </atomic-product-field-condition> <atomic-product-field-condition if-defined="ec_rating"> <atomic-product-rating field="ec_rating"></atomic-product-rating> </atomic-product-field-condition> </atomic-product-section-metadata> <atomic-product-section-emphasized> <atomic-product-price></atomic-product-price> </atomic-product-section-emphasized> <atomic-product-section-children> <atomic-product-children></atomic-product-children> </atomic-product-section-children> </template> </atomic-product-template> </atomic-commerce-search-box-instant-products> </atomic-commerce-search-box> </atomic-layout-section> <atomic-layout-section section="facets"><atomic-commerce-facets></atomic-commerce-facets></atomic-layout-section> <atomic-layout-section section="main"> <atomic-layout-section section="status"> <atomic-commerce-breadbox></atomic-commerce-breadbox> <atomic-commerce-query-summary></atomic-commerce-query-summary> <atomic-commerce-sort-dropdown></atomic-commerce-sort-dropdown> <atomic-commerce-did-you-mean></atomic-commerce-did-you-mean> <atomic-commerce-refine-toggle></atomic-commerce-refine-toggle> </atomic-layout-section> <atomic-layout-section section="products"> <atomic-commerce-product-list display="grid" density="compact" image-size="small"> <atomic-product-template> <template> <atomic-product-section-name id="product-name-section"> <atomic-product-link></atomic-product-link> </atomic-product-section-name> <atomic-product-section-visual> <atomic-product-field-condition if-defined="ec_thumbnails"> <atomic-product-image field="ec_thumbnails"></atomic-product-image> </atomic-product-field-condition> </atomic-product-section-visual> <atomic-product-section-metadata> <atomic-product-field-condition if-defined="ec_brand"> <atomic-product-text field="ec_brand"></atomic-product-text> </atomic-product-field-condition> <atomic-product-field-condition if-defined="ec_rating"> <atomic-product-rating field="ec_rating"></atomic-product-rating> </atomic-product-field-condition> </atomic-product-section-metadata> <atomic-product-section-emphasized> <atomic-product-price></atomic-product-price> </atomic-product-section-emphasized> <atomic-product-section-description> <atomic-product-excerpt></atomic-product-excerpt> </atomic-product-section-description> <atomic-product-section-children> <atomic-product-children></atomic-product-children> </atomic-product-section-children> </template> </atomic-product-template> </atomic-commerce-product-list> <atomic-commerce-query-error></atomic-commerce-query-error> <atomic-commerce-no-products></atomic-commerce-no-products> </atomic-layout-section> <atomic-layout-section section="pagination"> <atomic-commerce-load-more-products></atomic-commerce-load-more-products> </atomic-layout-section> </atomic-layout-section> </atomic-commerce-layout> </atomic-commerce-interface>
The ec_thumbnails
standard commerce field contains the product images used by the image components in this example. Adjust this property if your product thumbnails are stored in a different field. -
Save your changes and preview the search page in your storefront to confirm that results and components render as expected.
Standalone search box
You’ll probably want to include a standalone search box on every page of your website other than the search page, which already includes a search box. To implement it, do the following:
-
Create a snippet named
initialize-ssb.liquid
to initialize the standalone search box.<!-- snippets/initialize-ssb.liquid --> <script type="module"> window.handleCoveoSearchboxClick = async function () { const [response] = await Promise.all([ fetch('/apps/coveo?marketId={{ localization.market.id | encode_url_component }}'), window.loadAtomic(), ]); const config = await response.json(); const engine = await window.configureHeadless(config); const standaloneSearchBox = document.querySelector('#standalone-search-box'); if (!standaloneSearchBox.hasAttribute('atomic-initialized')) { standaloneSearchBox.initializeWithEngine(engine); standaloneSearchBox.setAttribute('atomic-initialized', 'true');
} }; </script>
This ensures that the standalone search box is only initialized once. -
Open your theme and locate the code that renders search boxes.
NoteIn the Dawn theme, this code is located in
sections/header-search.liquid
. -
Remove the existing search box code from this file.
-
Add the following code:
<!--- snippets/header-search.liquid ---> {% if request.path != '/search' %}
{% render 'atomic' %} {% render 'engine' %} {% render 'initialize-ssb' %} <details-modal> <details> <summary class="header__icon header__icon--search header__icon--summary link focus-inset modal__toggle" aria-haspopup="dialog" > <button id="search-button" aria-label="Search" class="svg-wrapper" onclick="handleCoveoSearchboxClick()"> <span class="svg-wrapper"> {{- 'icon-search.svg' | inline_asset_content -}}
</span> </button> </summary> <div class="search-modal modal__content gradient" role="dialog" aria-modal="true" > <div class="modal-overlay"></div> <div tabindex="-1" class="standalone-search-box-container" > <atomic-commerce-interface type="search" id="standalone-search-box"> <atomic-layout-section section="search"> <atomic-commerce-search-box redirection-url="/search"> <atomic-commerce-search-box-recent-queries></atomic-commerce-search-box-recent-queries> <atomic-commerce-search-box-query-suggestions></atomic-commerce-search-box-query-suggestions> <atomic-commerce-search-box-instant-products image-size="small"> <atomic-product-template> <template> <atomic-product-section-name> <atomic-product-link></atomic-product-link> </atomic-product-section-name> <atomic-product-section-visual> <atomic-product-image field="ec_thumbnails"></atomic-product-image> </atomic-product-section-visual> <atomic-product-section-metadata> <atomic-product-text field="ec_brand"></atomic-product-text> <atomic-product-rating field="ec_rating"></atomic-product-rating> </atomic-product-section-metadata> <atomic-product-section-emphasized> <atomic-product-price></atomic-product-price> </atomic-product-section-emphasized> <atomic-product-section-children> <atomic-product-children></atomic-product-children> </atomic-product-section-children> </template> </atomic-product-template> </atomic-commerce-search-box-instant-products> </atomic-commerce-search-box> </atomic-layout-section> </atomic-commerce-interface> <div class="hidden"><input></div>
<button type="button" class="search-modal__close-button modal__close-button link link--text focus-inset" aria-label="{{ 'accessibility.close' | t }}" > <span class="svg-wrapper"> {{- 'icon-close.svg' | inline_asset_content -}} </span> </button> </div> </div> </details> </details-modal> {% endif %}
Don’t display the standalone search box on the search page, since it already includes a search box. This image is from the Dawn theme. Replace it with the appropriate SVG icon for your theme, as needed. This div
and the followingbutton
are carried over from the Dawn theme for modal handling. Customize this markup to fit your theme, as needed. -
Style your standalone search box. The CSS classes and styles you apply to your standalone search box depend on your theme.
-
In the Dawn theme, create an
ssb.css
file in theassets
folder and include the following:/* assets/ssb.css */ .standalone-search-box-container { width: 80%; display: flex; justify-content: center; } #search-button { background: white; border: white; cursor: pointer; } #search-button:hover { transform: scale(1.07); }
-
Add the following to render the CSS in
header-search.liquid
:{{ 'ssb.css' | asset_url | stylesheet_tag }}
-
-
Save your changes and preview the standalone search box in your storefront to confirm that it renders as expected.
Product listing page
To implement a Coveo Atomic product listing page (PLP) or replace an existing PLP with an Atomic one, do the following:
-
Create the target listing configurations. Currently, the Coveo AI Search & Discovery app doesn’t create listing configurations automatically. You must create them yourself, regardless of whether you’re using the app.
-
Create a snippet named
initialize-plp.liquid
to initialize the Atomic interface.<!-- snippets/initialize-plp.liquid --> <script type="module"> const [response] = await Promise.all([ fetch('/apps/coveo?marketId={{ localization.market.id | encode_url_component }}'), window.loadAtomic(), ]); const config = await response.json(); const engine = await window.configureHeadless(config); const productList = document.querySelector('#product-listing'); await productList.initializeWithEngine(engine); productList.executeFirstRequest(); </script>
-
Open your theme and locate the code that renders product listing pages.
NoteIn the Dawn theme, an example file with this code is
sections/collection-template.liquid
. -
Remove the existing product listing page code from this file.
-
Add the initialization snippets and an
atomic-commerce-interface
element of typeproduct-listing
that includes the target Atomic product listing page components, as follows:{% render 'engine' %} {% render 'atomic' %} {% render 'initialize-plp' %} <atomic-commerce-interface id="product-listing" type="product-listing"> <atomic-commerce-layout> <atomic-layout-section section="facets"><atomic-commerce-facets></atomic-commerce-facets></atomic-layout-section> <atomic-layout-section section="main"> <atomic-layout-section section="status"> <atomic-commerce-breadbox></atomic-commerce-breadbox> <atomic-commerce-query-summary></atomic-commerce-query-summary> <atomic-commerce-sort-dropdown></atomic-commerce-sort-dropdown> <atomic-commerce-refine-toggle></atomic-commerce-refine-toggle> </atomic-layout-section> <atomic-layout-section section="products"> <atomic-commerce-product-list display="grid" density="compact" image-size="small"> <atomic-product-template> <template> <atomic-product-section-name> <atomic-product-link></atomic-product-link> </atomic-product-section-name> <atomic-product-section-visual> <atomic-product-image field="ec_thumbnails"></atomic-product-image> </atomic-product-section-visual> <atomic-product-section-children> <atomic-product-children></atomic-product-children> </atomic-product-section-children> <atomic-product-section-metadata> <atomic-product-text field="ec_brand"></atomic-product-text> <atomic-product-rating field="ec_rating"></atomic-product-rating> </atomic-product-section-metadata> <atomic-product-section-emphasized> <atomic-product-price></atomic-product-price> </atomic-product-section-emphasized> <atomic-product-section-description> <atomic-product-description></atomic-product-description> </atomic-product-section-description> </template> </atomic-product-template> </atomic-commerce-product-list> <atomic-commerce-query-error></atomic-commerce-query-error> <atomic-commerce-no-products></atomic-commerce-no-products> </atomic-layout-section> <atomic-layout-section section="pagination"> <atomic-commerce-load-more-products></atomic-commerce-load-more-products> </atomic-layout-section> </atomic-layout-section> </atomic-commerce-layout> </atomic-commerce-interface>
-
Save your changes and preview the product listing page in your storefront to confirm that products and components render as expected.
Recommendations
To implement Coveo Atomic recommendations or replace your existing recommendations, do the following:
-
Create the target recommendation configurations. Currently, the Coveo AI Search & Discovery app doesn’t create recommendation configurations automatically. You must create them yourself, regardless of whether you’re using the app.
-
Create a snippet named
initialize-recs.liquid
to initialize the Atomic interface.<!-- snippets/initialize-recs.liquid --> <script type="module"> const [response] = await Promise.all([ fetch('/apps/coveo?marketId={{ localization.market.id | encode_url_component }}'), window.loadAtomic() ]); const config = await response.json(); const engine = await window.configureHeadless(config); const recs = document.querySelector('#recs-component'); recs.initializeWithEngine(engine); </script>
-
Open your theme and locate the code that renders recommendations.
NoteIn the Dawn theme, an example file with this code is
featured-collection.liquid
. -
Remove the existing recommendation code from this file.
-
Add the initialization snippets and an
atomic-commerce-recommendation-interface
element that includes the target Atomic recommendation components, as follows:{% render 'engine' %} {% render 'atomic' %} {% render 'initialize-recs' %} <atomic-commerce-recommendation-interface class="page-width" id="recs-component"> <atomic-commerce-layout> <atomic-layout-section section="main"> <atomic-commerce-recommendation-list slot-id=<SLOT_ID>
> <atomic-product-template> <template> <atomic-product-section-name> <atomic-product-link></atomic-product-link> </atomic-product-section-name> <atomic-product-section-visual> <atomic-product-image field="ec_thumbnails"></atomic-product-image> </atomic-product-section-visual> <atomic-product-section-metadata> <atomic-product-field-condition if-defined="ec_brand"> <atomic-product-text field="ec_brand"></atomic-product-text> </atomic-product-field-condition> <atomic-product-field-condition if-defined="ec_brand"> <atomic-product-rating field="ec_rating"></atomic-product-rating> </atomic-product-field-condition> </atomic-product-section-metadata> <atomic-product-section-description> <atomic-product-text field="ec_shortdesc"></atomic-product-text> </atomic-product-section-description> <atomic-product-section-emphasized> <atomic-product-price></atomic-product-price> </atomic-product-section-emphasized> <atomic-product-section-children> <atomic-product-children></atomic-product-children> </atomic-product-section-children> </template> </atomic-product-template> </atomic-commerce-recommendation-list> </atomic-layout-section> </atomic-commerce-layout> </atomic-commerce-recommendation-interface>
Replace <SLOT_ID>
with the target slot ID for your recommendations. The slot ID is returned when you create recommendation configurations. -
Save your changes and preview the recommendations in your storefront to confirm that they render as expected.
What’s next?
To explore the available Coveo Atomic Commerce components and options, see the reference documentation.
To learn more about the Atomic library, see the Atomic documentation.