Reference
Shopify E-commerce
The engine builds the Content Security Policy from
site.yaml. Adding Shopify hosts toallowedDomainsopensscript-src,frame-src, andconnect-srcso the Storefront components can load and reach Shopify's APIs.
Add Shopify products and checkout to any page using Storefront Web Components. Shopify handles inventory, payments, and checkout — the site just displays products.
Setup
- Get store domain from the user (e.g.
mystore.myshopify.com) - Add domains to CSP in
data/site.yaml:{ "allowedDomains": ["cdn.shopify.com", "mystore.myshopify.com"] } - Add the script to the page or layout:
<script src="https://cdn.shopify.com/storefront/web-components.js"></script> - Initialize the store (once per page, before any product components):
<shopify-store id="store" store-domain="https://mystore.myshopify.com"></shopify-store>
Optional: add public-access-token="xxxxx" for inventory data or custom fields.
Display a Product
Use <shopify-context> with the product handle (the URL slug from Shopify admin):
<shopify-context type="product" handle="classic-leather-bag">
<template>
<div class="grid md:grid-cols-2 gap-8">
<shopify-media query="product.selectedOrFirstAvailableVariant.image" width="600"></shopify-media>
<div>
<h2 class="text-2xl font-bold"><shopify-data query="product.title"></shopify-data></h2>
<p class="text-xl font-semibold text-primary-600 mt-2">
<shopify-money query="product.selectedOrFirstAvailableVariant.price"></shopify-money>
</p>
<div class="mt-4"><shopify-data query="product.descriptionHtml"></shopify-data></div>
<shopify-variant-selector class="mt-4"></shopify-variant-selector>
<button onclick="getElementById('store').buyNow(event)"
shopify-attr--disabled="!product.selectedOrFirstAvailableVariant.availableForSale"
class="mt-6 px-8 py-3 bg-primary-600 text-white font-semibold rounded-lg hover:bg-primary-700 transition-colors disabled:opacity-50">
Buy now
</button>
</div>
</div>
</template>
</shopify-context>
Buy Now Button (single product → checkout)
The simplest pattern — one button that goes straight to Shopify checkout:
<shopify-context type="product" handle="product-handle">
<template>
<button onclick="getElementById('store').buyNow(event)"
shopify-attr--disabled="!product.selectedOrFirstAvailableVariant.availableForSale"
class="px-6 py-3 bg-primary-600 text-white rounded-lg font-semibold hover:bg-primary-700 disabled:opacity-50">
Buy now — <shopify-money query="product.selectedOrFirstAvailableVariant.price"></shopify-money>
</button>
</template>
</shopify-context>
Add to Cart (multi-product shopping)
For stores with multiple products — customers add items to a cart, then check out:
<!-- Product card -->
<shopify-context type="product" handle="product-handle">
<template>
<shopify-media query="product.selectedOrFirstAvailableVariant.image" width="400"></shopify-media>
<h3><shopify-data query="product.title"></shopify-data></h3>
<shopify-money query="product.selectedOrFirstAvailableVariant.price"></shopify-money>
<shopify-variant-selector></shopify-variant-selector>
<button onclick="getElementById('cart').addLine(event).showModal()"
shopify-attr--disabled="!product.selectedOrFirstAvailableVariant.availableForSale"
class="px-4 py-2 bg-primary-600 text-white rounded-lg text-sm">
Add to cart
</button>
</template>
</shopify-context>
<!-- Cart (one per page, renders as a dialog) -->
<shopify-cart id="cart"></shopify-cart>
Shop Pay Button (fastest checkout)
For a branded Shop Pay express checkout button:
<script src="https://cdn.shopify.com/shopifycloud/shop-js/modules/v2/loader.pay-button.esm.js" type="module"></script>
<shop-pay-button
store-url="https://mystore.myshopify.com"
variants="36607875587"
></shop-pay-button>
Multiple variants with quantities: variants="36607875587:2,36607875459:3"
Product Collection Grid
Display multiple products in a grid layout:
<div class="grid md:grid-cols-3 gap-6">
<shopify-context type="product" handle="product-1">
<template>
<div class="border rounded-xl overflow-hidden">
<shopify-media query="product.selectedOrFirstAvailableVariant.image" width="400" class="w-full"></shopify-media>
<div class="p-4">
<h3 class="font-semibold"><shopify-data query="product.title"></shopify-data></h3>
<p class="text-primary-600 font-bold mt-1">
<shopify-money query="product.selectedOrFirstAvailableVariant.price"></shopify-money>
</p>
<button onclick="getElementById('cart').addLine(event)"
class="mt-3 w-full py-2 bg-primary-600 text-white rounded-lg text-sm">
Add to cart
</button>
</div>
</div>
</template>
</shopify-context>
<!-- Repeat for each product -->
</div>
<shopify-cart id="cart"></shopify-cart>
Data Display Components
| Component | Purpose | Example |
|---|---|---|
<shopify-data> |
Text content | query="product.title" |
<shopify-money> |
Formatted price | query="product.selectedOrFirstAvailableVariant.price" |
<shopify-media> |
Product image | query="product.selectedOrFirstAvailableVariant.image" width="400" |
<shopify-variant-selector> |
Size/color picker | No query needed |
<shopify-cart> |
Shopping cart dialog | Add id="cart" for JS access |
Rules
- Always ask for the store domain — never invent it. Product handles come from Shopify admin (the URL slug).
- No access token is needed for basic product display — only for inventory or custom data.
- Shopify handles checkout, inventory, and payment — the site just displays products.
- Web components are client-side rendered — product content is NOT indexed by search engines. Add static SEO content (headings, descriptions) alongside the components.
- Cart does NOT support mixing products from multiple Shopify stores.
- Style components with regular CSS — they don't use Shadow DOM for most elements.
- Place
<shopify-store>and<shopify-cart>once per page, before product contexts. - The
<template>inside<shopify-context>renders after data loads — content is hidden until ready.