How to add Web Experiment to a Shopify store

Test Shopify storefront offer copy with Web Experiment. Set consent before loading, preserve the product link, and verify impressions and clicks.

  • Shopify
  • Online Store theme
  • Amplitude app
  • 25 min

Chris Tam · Last updated 19 October 2026

A Shopify offer can send shoppers to the same product with different copy. Web Experiment lets you compare that change against the storefront action you want to improve.

In this tutorial, I'll target the shared offer, configure its consent and loading order, and preview two text variants against an offer-click metric.

What you'll build

A storefront-only offer copy experiment with a stable selector, two explicit text actions, and Shopify Offer Clicked as its primary metric.

Prerequisites

  • An Online Store Shopify test store and permission to install the Amplitude app and edit a duplicated theme. These tutorials target a Liquid theme, not a headless Hydrogen storefront.
  • A test product and an Amplitude test project. Get its public API key in Settings > Projects > your project > General > Manage next to API Key. Admins and Managers can view it.
  • A storefront privacy configuration that permits this synthetic test. Check initial refusal, grant, and withdrawal on the actual store. The custom code below controls only its own additions; it does not configure or stop the app's collection.
  • Before using browser snippets, confirm the app exposes Browser SDK 2 methods on window.amplitude in the top storefront window. Check the installed version in Instrumentation Explorer. Do not add another initializer to make a missing global appear.

An Amplitude account and Shopify test store are required. Sign up for Amplitude; check the product limits for your project before testing.

Keep the app and offer-click handler from the earlier Shopify tutorials. The app includes Web Experiment. Its implementation reference specifically recommends the asynchronous script with anti-flicker handling for Shopify's loading method.

Create snippets/amp-shopify-experiment.liquid below. Replace the public key with the same project key connected in the app. For an EU project, use the EU CDN host noted in the code. Render the snippet high in the head, before content_for_header, without replacing the theme's existing head or app content.

snippets/amp-shopify-experiment.liquid
{% comment %}
snippets/amp-shopify-experiment.liquid
Render high in head, before content_for_header; preserve all existing theme imports and app content.
This gates Web Experiment only. Verify the app's analytics/replay consent separately.
{% endcomment %}
{% assign amp_build_api_key = 'AMPLITUDE_API_KEY' %}
<script>
window.experimentConfig = {
  ...window.experimentConfig,
  consentOptions: { consentRequired: true, consentStatus: 'pending' }
};
(function (d, h) {
  var apiKey = {{ amp_build_api_key | json }};
  var timeout = 1000;
  var id = 'amp-exp-css';
  try {
    if (!d.getElementById(id)) {
      var st = d.createElement('style');
      st.id = id;
      st.innerText = '* { visibility: hidden !important; background-image: none !important; }';
      h.appendChild(st);
      window.setTimeout(function () { st.remove(); }, timeout);
      var sc = d.createElement('script');
      // For an EU Amplitude project use https://cdn.eu.amplitude.com/script/ instead.
      sc.src = 'https://cdn.amplitude.com/script/' + apiKey + '.experiment.js';
      sc.async = true;
      sc.onerror = function () { st.remove(); };
      h.insertBefore(sc, d.currentScript || h.lastChild);
    }
  } catch (e) { console.error(e); }
})(document, document.head);
</script>
layout/theme.liquid
{% comment %} layout/theme.liquid: insert high in head before existing content_for_header; preserve the rest. {% endcomment %}
{% render 'amp-shopify-experiment' %}

The timeout and error handler clear the page mask if the experiment script is slow or blocked. Pending consent can apply variants in memory while buffering impressions; denial can retain a refusal-marker cookie. This Web Experiment gate does not defer the app's analytics or replay. Verify those through the store's actual privacy integration before customer traffic.

2. Connect Shopify's permission decision

Create assets/amp-shopify-experiment-consent.js. Add its asset tag once near the closing body, after Shopify's storefront globals are available. It records a permission decision for a later-loading experiment script and also updates one already loaded.

assets/amp-shopify-experiment-consent.js
// assets/amp-shopify-experiment-consent.js
// Preserve app analytics/replay, amp-shopify-events.js, guides, and stable offer attributes.
(() => {
  if (window.__ampShopifyExperimentConsent) return;
  window.__ampShopifyExperimentConsent = true;
  const apply = () => {
    const privacy = window.Shopify?.customerPrivacy;
    if (!privacy) return;
    const status = privacy.analyticsProcessingAllowed() ? 'granted' : 'denied';
    // Queue a decision for a script that has not loaded yet, and update a loaded SDK.
    window.experimentConfig.consentOptions.consentStatus = status;
    window.webExperiment?.setConsentStatus(status);
  };
  document.addEventListener('visitorConsentCollected', apply);
  window.Shopify?.loadFeatures?.([{ name: 'consent-tracking-api', version: '0.2' }], error => {
    if (!error) apply();
  });
})();
layout/theme.liquid
{% comment %} layout/theme.liquid: add near closing body, preserving events and guides scripts. {% endcomment %}
<script src="{{ 'amp-shopify-experiment-consent.js' | asset_url }}" defer></script>

Use the Customer Privacy API decision only after confirming it represents the purpose your store assigns to this experiment. Check initial refusal, grant, withdrawal, and decisions that arrive before the experiment SDK. Keep the pending state if the privacy API fails. Don't rewrite SDK storage or grant permission just to make a preview pass.

3. Configure the copy test and metric

Create a Web Experiment for the actual storefront home URL. Use Element Changes and target the offer's nested span. Commit the selector edit before changing copy and verify the saved action history. Keep the link and its analytics and guide attributes unchanged.

Visual Editor selector
/* Visual Editor selector */
[data-experiment="shopify-offer"] span
Saved experiment configuration
# Saved experiment configuration
Control text action: Explore this product
Treatment text action: Find your next favorite
Primary metric: unique users performing Shopify Offer Clicked
Ordinary rollout during QA: 0%
Scope: the storefront page containing the offer section

Save an explicit text action for each variant, including the original control text. The tracking reference ties impressions to applied actions. Confirm the control action in saved history; correct-looking original copy alone is insufficient. In Metrics, create a custom Unique conversions metric, Add Event, select the observed Shopify Offer Clicked event, then Save and Close. This measures a click, not an order.

4. Preview both variants and check tracking

Use Test & Preview separately for control and treatment. Confirm the visible copy, unchanged product destination, one custom offer click, and the required impression. The ingestion transformation changes $impression into [Experiment] Impression with experiment flag and variant properties.

Browser Console on a consented preview visit after clicking the offer
// Browser Console on a consented preview visit after clicking the offer
window.amplitude.getDeviceId();
window.amplitude.flush();

Test the anti-flicker timeout and blocked-script path as well as successful loading. The page must become visible in either failure case. Allow the documented preview cache delay, up to 60 seconds, after configuration changes. Inspect for unexpected duplicate experiment activity from app and theme loading. Keep rollout at 0% until impressions, consent, and the primary click metric pass. Preview is not an experiment result, and this tutorial does not run a checkout experiment.

MCP: Ask your coding agent to inspect the project's event taxonomy with the Amplitude MCP server before adding custom events. Reuse an existing name when it describes the same action.

Verify it worked

  1. Storefront: Open the actual storefront URL while logged out. Inspect the app's script and any theme additions. Use one app-owned analytics installation; a pixel sandbox and the top storefront window are different contexts.
  2. Delivery: In browser Network, confirm event POSTs return HTTP 200 from api2.amplitude.com for US or api.eu.amplitude.com for EU. Compressed requests are not a reliable place to read event contents.
  3. Instrumentation Explorer: Inspect event names, properties, counts, and device_id in the Chrome extension. Compare one action with one expected event.
  4. User Profiles: Search that device ID in Users > User Profiles and inspect its event stream. The docs describe ten seconds to one minute for events. Ingestion Debugger provides request and event counts, not individual payloads.

Preview both explicit text actions and inspect their ingested impressions and custom offer clicks.

Troubleshooting

The storefront stays hidden

Check that the exact timeout/error cleanup exists and test a blocked script. Keep security-policy changes with the owner of that policy; do not disable protections as a tutorial shortcut.

Nothing appears after connecting the app

Check the connected project key, storefront privacy decision, app installation, script requests, and browser blockers. Use the storefront's top window when checking window.amplitude; a sandboxed custom pixel is a separate environment.

Events appear twice

Look for a second SDK in a custom pixel, tag manager, theme snippet, or older integration. Remove the duplicate installation through its supported configuration. Keep the app-owned SDK and the existing theme code.

Theme editing changes the result

Test the actual storefront URL as well as the duplicated theme preview. Confirm the same product, template, hostname, and privacy state. Theme editor rendering alone is not a hosted-store verification.

FAQ

Why is there an experiment script when the app includes Web Experiment?

The Shopify implementation docs prescribe the early asynchronous anti-flicker snippet because of the app's loading method. Keep that documented setup and test the actual app/theme combination; do not add another analytics initializer.

Is a click metric a revenue metric?

Shopify Offer Clicked measures the action this text test changes directly. Validate a separate commerce metric before using it to claim an order or revenue effect.

Full code reference

Unchanged from earlier pages; keep your versions: App installation, sections/amplitude-build-offer.liquid, assets/amp-shopify-events.js, assets/amp-shopify-guides.js.

snippets/amp-shopify-experiment.liquid
{% comment %}
snippets/amp-shopify-experiment.liquid
Render high in head, before content_for_header; preserve all existing theme imports and app content.
This gates Web Experiment only. Verify the app's analytics/replay consent separately.
{% endcomment %}
{% assign amp_build_api_key = 'AMPLITUDE_API_KEY' %}
<script>
window.experimentConfig = {
  ...window.experimentConfig,
  consentOptions: { consentRequired: true, consentStatus: 'pending' }
};
(function (d, h) {
  var apiKey = {{ amp_build_api_key | json }};
  var timeout = 1000;
  var id = 'amp-exp-css';
  try {
    if (!d.getElementById(id)) {
      var st = d.createElement('style');
      st.id = id;
      st.innerText = '* { visibility: hidden !important; background-image: none !important; }';
      h.appendChild(st);
      window.setTimeout(function () { st.remove(); }, timeout);
      var sc = d.createElement('script');
      // For an EU Amplitude project use https://cdn.eu.amplitude.com/script/ instead.
      sc.src = 'https://cdn.amplitude.com/script/' + apiKey + '.experiment.js';
      sc.async = true;
      sc.onerror = function () { st.remove(); };
      h.insertBefore(sc, d.currentScript || h.lastChild);
    }
  } catch (e) { console.error(e); }
})(document, document.head);
</script>
assets/amp-shopify-experiment-consent.js
// assets/amp-shopify-experiment-consent.js
// Preserve app analytics/replay, amp-shopify-events.js, guides, and stable offer attributes.
(() => {
  if (window.__ampShopifyExperimentConsent) return;
  window.__ampShopifyExperimentConsent = true;
  const apply = () => {
    const privacy = window.Shopify?.customerPrivacy;
    if (!privacy) return;
    const status = privacy.analyticsProcessingAllowed() ? 'granted' : 'denied';
    // Queue a decision for a script that has not loaded yet, and update a loaded SDK.
    window.experimentConfig.consentOptions.consentStatus = status;
    window.webExperiment?.setConsentStatus(status);
  };
  document.addEventListener('visitorConsentCollected', apply);
  window.Shopify?.loadFeatures?.([{ name: 'consent-tracking-api', version: '0.2' }], error => {
    if (!error) apply();
  });
})();

Hand it to your agent

Give your agent this tutorial and the existing theme files. Keep the app installation, all sibling theme additions, and the selected test product.

Agent prompt
Agent task: Shopify web experiment.
Use the official Amplitude Shopify app as installation owner. Merge only this page's additions; preserve content_for_header, product/cart templates, replay privacy, offer attributes, custom event handlers, and consent. Confirm actual Browser SDK methods before using snippets. Test consent and the promised outcome on a real Shopify test store. Report SDK delivery, native Shopify behavior, product configuration, and processed results separately. Do not call another analytics init or claim checkout behavior from a storefront test.

What this unlocks

Product analytics compares Shopify Offer Clicked with the app's observed product and cart events after both are verified.

Session replay shows the storefront interaction after capture, identity, and privacy checks pass.

Web Experiment uses the observed Shopify Offer Clicked event as the offer-copy test's primary metric.

Guides and surveys can ask what product information a visitor needs after the separate SDK and targeting are configured.

Next steps

Add Amplitude to your Shopify site

Connect the official Shopify app and follow the tutorials for each product.