Shopify

Shopify is an eCommerce platform that provides tools to help online sellers run their businesses.  

Fullstory can easily be set up on most Shopify themes by following the steps below. Once installed, you can use Fullstory to play back user sessions, build conversion funnels, and gain new insights into how your customers navigate your store. 

Installation

    1. Log in to your Shopify account, and open your admin panel.

    2. Select “Online Store” under the “Sales Channels” section of the admin panel.

      1.png 

    3. On the “Themes” section of the panel, click the Actions button in the upper right-hand corner of the page to reveal a dropdown.

    4. Click “Edit code”.
      2.png

    5. Click “theme.liquid” under the “Layout” section.*
      3.png

    6. Paste your Fullstory data capture snippet (learn how to find it here) into the <head> of the theme.
      4.png

 

* If you’re using a custom theme, it’s possible that the theme file has a name other than "theme.liquid". Check with the theme’s developer to verify.

Sending Shopify analytics events to Fullstory

In addition to adding the Fullstory snippet to your theme as described, you can send Shopify-provided analytics events to Fullstory.

Note: Shopify now recommends merchants and third parties upgrade to Checkout Extensibility.  Doing so affects previous Fullstory integration with the following notable changes per Shopify:

  • On August 13, 2024, checkout.liquid for the information, shipping, and payment pages will be turned off and no longer supported.
  • On August 28, 2025, checkout.liquid for the thank you and order status pages will be turned off and no longer supported. This also includes turning off apps using script tags and additional scripts under the post-purchase and order status pages. Shopify Scripts will continue to work alongside checkout extensibility until this date.
  • The checkout object will be deprecated for the Information, Shipping, and Payment pages on August 13, 2024.

To accommodate Checkout Extensibility, Fullstory provides shop administrators:

  1. A recommended Order Status page script that reliably sends a data-rich “Order Completed” event.
  2. A custom web pixel that captures newly available analytics events.  A shop admin can configure the custom web pixel. It works out-of-the-box with no custom development required.

Adding your data capture snippet to your Order Status page

Install the additional script by following the Tracking order steps provided by Shopify:

  1. From your Shopify admin, go to Settings > Checkout.
  2. Copy and paste the additional script code from this article.  If you have existing scripts, prepend the Fullstory script to the beginning of the existing scripts.
  3. Replace the text <org id> in the script’s code with your Org Id. To locate your Org Id, see the article How do I find my Fullstory Org Id.  The resulting line should appear similar to window._fs_org = 'o-0rGId-na1';.
  4. Click the Save button.

Order Status page script

<script>
window['_fs_host'] = 'fullstory.com';
window['_fs_script'] = 'edge.fullstory.com/s/fs.js';
window['_fs_org'] = '<org id>';
window['_fs_namespace'] = 'FS';
!function(m,n,e,t,l,o,g,y){var s,f,a=function(h){
return!(h in m)||(m.console&&m.console.log&&m.console.log('FullStory namespace conflict. Please set window["_fs_namespace"].'),!1)}(e)
;function p(b){var h,d=[];function j(){h&&(d.forEach((function(b){var d;try{d=b[h[0]]&&b[h[0]](h[1])}catch(h){return void(b[3]&&b[3](h))}
d&&d.then?d.then(b[2],b[3]):b[2]&&b[2](d)})),d.length=0)}function r(b){return function(d){h||(h=[b,d],j())}}return b(r(0),r(1)),{
then:function(b,h){return p((function(r,i){d.push([b,h,r,i]),j()}))}}}a&&(g=m[e]=function(){var b=function(b,d,j,r){function i(i,c){
h(b,d,j,i,c,r)}r=r||2;var c,u=/Async$/;return u.test(b)?(b=b.replace(u,""),"function"==typeof Promise?new Promise(i):p(i)):h(b,d,j,c,c,r)}
;function h(h,d,j,r,i,c){return b._api?b._api(h,d,j,r,i,c):(b.q&&b.q.push([h,d,j,r,i,c]),null)}return b.q=[],b}(),y=function(b){function h(h){
"function"==typeof h[4]&&h[4](new Error(b))}var d=g.q;if(d){for(var j=0;j<d.length;j++)h(d[j]);d.length=0,d.push=h}},function(){
(o=n.createElement(t)).async=!0,o.crossOrigin="anonymous",o.src="https://"+l,o.onerror=function(){y("Error loading "+l)}
;var b=n.getElementsByTagName(t)[0];b&&b.parentNode?b.parentNode.insertBefore(o,b):n.head.appendChild(o)}(),function(){function b(){}
function h(b,h,d){g(b,h,d,1)}function d(b,d,j){h("setProperties",{type:b,properties:d},j)}function j(b,h){d("user",b,h)}function r(b,h,d){j({
uid:b},d),h&&j(h,d)}g.identify=r,g.setUserVars=j,g.identifyAccount=b,g.clearUserCookie=b,g.setVars=d,g.event=function(b,d,j){h("trackEvent",{
name:b,properties:d},j)},g.anonymize=function(){r(!1)},g.shutdown=function(){h("shutdown")},g.restart=function(){h("restart")},
g.log=function(b,d){h("log",{level:b,msg:d})},g.consent=function(b){h("setIdentity",{consent:!arguments.length||b})}}(),s="fetch",
f="XMLHttpRequest",g._w={},g._w[f]=m[f],g._w[s]=m[s],m[s]&&(m[s]=function(){return g._w[s].apply(this,arguments)}),g._v="2.0.0")
}(window,document,window._fs_namespace,"script",window._fs_script);
</script>
{% if first_time_accessed %}
<script>
(function () {
  window[window['_fs_namespace']]('observe', {
    type: 'start',
    callback: () => {
      window[window['_fs_namespace']]('trackEvent', {
        name: 'Order Completed',
        properties: {
          cancel_reason: '{{ order.cancel_reason }}',
          cancelled: !!'{{ order.cancelled }}',
          confirmation_number: '{{ order.confirmation_number }}',
          financial_status: '{{ order.financial_status }}',
          fulfillment_status: '{{ order.fulfillment_status }}',
          id: '{{ order.id }}',
          item_count: +'{{ order.item_count }}',
          line_items_subtotal_price: +'{{ order.line_items_subtotal_price | money_without_currency }}',
          name: '{{ order.name }}',
          note: '{{ order.note }}',
          order_number: '{{ order.order_number }}',
          shipping_price: +'{{ order.shipping_price | money_without_currency }}',
          subtotal_price: +'{{ order.subtotal_price | money_without_currency }}',
          tax_price: +'{{ order.tax_price | money_without_currency }}',
          total_discounts: +'{{ order.total_discounts | money_without_currency }}',
          total_duties: +'{{ order.total_duties | money_without_currency }}',
          total_net_amount: +'{{ order.total_net_amount | money_without_currency }}',
          total_price: +'{{ order.total_price | money_without_currency }}',
          total_refunded_amount: +'{{ order.total_refunded_amount | money_without_currency }}',
        }
      });
    }
  })
}());
</script>
{% endif %}

When Shopify turns off the additional scripts feature on August 28, 2025, the “Order Completed” event will be provided by the pixel described in the next section.  

Adding the Fullstory Checkout Extensibility pixel to Shopify

Install the pixel by following the Add a custom pixel steps provided by Shopify:

  1. From your Shopify admin, go to Settings > Customer events.
  2. Click the Add custom pixel button.
  3. Name the pixel Fullstory Checkout Extension or similar.
  4. Click the Add pixel button.
  5. Copy and paste the pixel’s JavaScript code from this article.
  6. Replace the text <org id> in the pixel’s code with your Org Id. To locate your Org Id, see the article How do I find my Fullstory Org Id.  The resulting line should appear similar to window._fs_org = 'o-0rGId-na1';.
  7. Click the Save button.
  8. Activate the pixel by clicking the Connect button.

Fullstory Checkout Extensibility pixel

window._fs_org = '<org id>';
window._fs_identify_customer = false;
window._fs_use_order_completed = false;
window._fs_additional_scripts_snippet = true;
window._fs_host = 'fullstory.com';
window._fs_namespace = 'FS';
window._fs_debug = false;
window._fs_script = !window._fs_debug ? 'edge.fullstory.com/s/fs.js' : 'edge.fullstory.com/s/fs-debug.js';
function isAdditionalScriptAllowed() {
  return Date.now() < new Date('2025-08-28');
}
function isCheckout() {
  return document.location.pathname.indexOf('/checkouts/') !== -1;
}
function isThankYou() {
  return document.location.pathname.indexOf('/thank_you') !== -1;
}
if (isCheckout() || isThankYou()) {
  if (isCheckout()) {
    window._fs_is_outer_script = true;
  } else {
    if (isAdditionalScriptAllowed() && window._fs_additional_scripts_snippet) {
      window._fs_capture_on_startup = false;
    } else {
      window._fs_is_outer_script = true;
    }
  }
} else {
  window._fs_run_in_iframe = true;
}
!function(m,n,e,t,l,o,g,y){var s,f,a=function(h){
return!(h in m)||(m.console&&m.console.log&&m.console.log('FullStory namespace conflict. Please set window["_fs_namespace"].'),!1)}(e)
;function p(b){var h,d=[];function j(){h&&(d.forEach((function(b){var d;try{d=b[h[0]]&&b[h[0]](h[1])}catch(h){return void(b[3]&&b[3](h))}
d&&d.then?d.then(b[2],b[3]):b[2]&&b[2](d)})),d.length=0)}function r(b){return function(d){h||(h=[b,d],j())}}return b(r(0),r(1)),{
then:function(b,h){return p((function(r,i){d.push([b,h,r,i]),j()}))}}}a&&(g=m[e]=function(){var b=function(b,d,j,r){function i(i,c){
h(b,d,j,i,c,r)}r=r||2;var c,u=/Async$/;return u.test(b)?(b=b.replace(u,""),"function"==typeof Promise?new Promise(i):p(i)):h(b,d,j,c,c,r)}
;function h(h,d,j,r,i,c){return b._api?b._api(h,d,j,r,i,c):(b.q&&b.q.push([h,d,j,r,i,c]),null)}return b.q=[],b}(),y=function(b){function h(h){
"function"==typeof h[4]&&h[4](new Error(b))}var d=g.q;if(d){for(var j=0;j<d.length;j++)h(d[j]);d.length=0,d.push=h}},function(){
(o=n.createElement(t)).async=!0,o.crossOrigin="anonymous",o.src="https://"+l,o.onerror=function(){y("Error loading "+l)}
;var b=n.getElementsByTagName(t)[0];b&&b.parentNode?b.parentNode.insertBefore(o,b):n.head.appendChild(o)}(),function(){function b(){}
function h(b,h,d){g(b,h,d,1)}function d(b,d,j){h("setProperties",{type:b,properties:d},j)}function j(b,h){d("user",b,h)}function r(b,h,d){j({
uid:b},d),h&&j(h,d)}g.identify=r,g.setUserVars=j,g.identifyAccount=b,g.clearUserCookie=b,g.setVars=d,g.event=function(b,d,j){h("trackEvent",{
name:b,properties:d},j)},g.anonymize=function(){r(!1)},g.shutdown=function(){h("shutdown")},g.restart=function(){h("restart")},
g.log=function(b,d){h("log",{level:b,msg:d})},g.consent=function(b){h("setIdentity",{consent:!arguments.length||b})}}(),s="fetch",
f="XMLHttpRequest",g._w={},g._w[f]=m[f],g._w[s]=m[s],m[s]&&(m[s]=function(){return g._w[s].apply(this,arguments)}),g._v="2.0.0")
}(window,document,window._fs_namespace,"script",window._fs_script);


var customer = init.data.customer;
if (window._fs_identify_customer && customer && customer.id) {
  window[window._fs_namespace]('setIdentity', {
    uid: customer.id,
    properties: {
      email: customer.email,
      displayName: customer.firstName + ' ' + customer.lastName,
      ordersCount: customer.ordersCount
    }
  });
}
function format(obj) {
  var keys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
  if (keys && keys.length > 0) {
    for (var key in obj) {
      if (keys.includes(key)) {
        delete obj[key];
      } else if (typeof obj[key] === 'object' && obj[key] !== null) {
        format(obj[key], keys);
      }
    }
  }
  return obj;
}
function trackEvent(name) {
  var properties = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  if (window._fs_debug) {
    console.log(Date.now() + ' ' + name + ' ' + JSON.stringify(properties));
  }
  window[window._fs_namespace]('trackEvent', {
    name: name,
    properties: properties
  }, 'shopify-pixel-v1');
}
if (isCheckout()) {
  analytics.subscribe('clicked', function (event) {
    trackEvent(event.name, format(event.data.element, ['value']));
  });
  analytics.subscribe('input_changed', function (event) {
    trackEvent(event.name, format(event.data.element, ['value']));
  });
}
analytics.subscribe('page_viewed', function (event) {
});
analytics.subscribe('cart_viewed', function (event) {
  trackEvent(event.name, format(event.data.cart, ['lines']));
});
analytics.subscribe('collection_viewed', function (event) {
  trackEvent(event.name, {
    productVariants: data.collection.productVariants.length,
    title: data.collection.title
  });
});
analytics.subscribe('product_viewed', function (event) {
  trackEvent(event.name, format(event.data.productVariant, ['image', 'url']));
});
analytics.subscribe('product_added_to_cart', function (event) {
  trackEvent(event.name, format(event.data.cartLine, ['image', 'url']));
});
analytics.subscribe('product_removed_from_cart', function (event) {
  trackEvent(event.name, format(event.data.cartLine, ['image', 'url']));
});
analytics.subscribe('search_submitted', function (event) {
  trackEvent(event.name, {
    productVariants: event.productVariants.length,
    query: event.query
  });
});
analytics.subscribe('checkout_started', function (event) {
  trackEvent(event.name);
});
analytics.subscribe('checkout_address_info_submitted', function (event) {
  trackEvent(event.name);
});
analytics.subscribe('checkout_contact_info_submitted', function (event) {
  trackEvent(event.name);
});
analytics.subscribe('checkout_shipping_info_submitted', function (event) {
  trackEvent(event.name);
});
analytics.subscribe('payment_info_submitted', function (event) {
  trackEvent(event.name);
});
analytics.subscribe('checkout_completed', function (event) {
  var checkout = event.data.checkout;
  var properties = {
    attributes: checkout.attributes,
    currencyCode: checkout.currencyCode,
    discountApplications: checkout.discountApplications.map(function (d) {
      return d.allocationMethod + ':' + d.targetSelection + ':' + d.targetType + ':' + d.type;
    }),
    discountTitles: checkout.discountApplications.map(function (d) {
      return d.title;
    }),
    lineItems: checkout.lineItems.length,
    orderId: checkout.order.id,
    subtotalPrice: checkout.subtotalPrice,
    totalPrice: checkout.totalPrice,
    totalTax: checkout.totalTax,
    transactionGateways: checkout.transactions.map(function (t) {
      return t.gateway;
    })
  };
  if (window._fs_use_order_completed || !isAdditionalScriptAllowed()) {
    properties.products = checkout.lineItems.map(function (l) {
      return {
        quantity: l.quantity,
        productTitle: l.title,
        price: l.variant.price,
        sku: l.variant.sku,
        variantTitle: l.variant.title
      };
    });
    trackEvent('Order Completed', properties);
  } else {
    trackEvent(event.name, properties);
  }
});

Fullstory custom events will now appear throughout session replays shortly after activating the pixel. More specifically, the following custom events have been instrumented.  Note that some events may have data incompatible with Fullstory and may be omitted.  Additionally, the pixel specifically omits the value property of Shopify DOM events as it could include sensitive or personal information.

Any theme page where the Fullstory snippet has been added:

Payments page:

Thank you page (after August 28, 2025):

Configure a Revenue Event

Since the pixel provides a conversion event, follow the steps to Choose Your Revenue Event in Settings, and choose either the “Order Completed” event.  Select either the total_price_real or totalPrice_real property available.

 

Known issues with the Fullstory Checkout Extensibility pixel

Due to technical limitations of the pixel sandbox provided by Shopify, the following issues are known to occur.

The Checkout (Information, Shipping, and Payment) pages will appear blank in session replay, but analytics events will still be captured. Fullstory session replays will show website content on all pages leading up to the Checkout page. Since the Checkout pages’ content is inaccessible to third parties, you will see a period of only custom events and blank pages in session replay. Of note, custom events accurately reflect the time and user activity of the Checkout pages.

If the thank you page has the Fullstory snippet added (prior to August 28, 2025), replay will resume showing website content once the user has been redirected to the thank you page. After August 28, 2025, the thank you page will similarly appear blank (with analytics events still captured).

The pixel requires the All other domains data capture setting to be On. Fullstory’s capture data by domain feature is unable to identify the pixel’s iframe origin and determine if the domain is allowed.  As a result, all domains must be allowed.

Page variables are not supported.  Because the pixel is contained in an iframe, using FS(‘setProperties’) is not supported.

As a best practice (for both pixel and historical integration), segments and metrics using custom events on the thank you page should add the Entry Page is false criteria.  When users become inactive on the thank you page and later active, Shopify may generate analytics events again.  The Entry Page is false criteria excludes sessions and events that are the result of the user resuming activity on the thank you page after their previous session has ended.

Configure the Fullstory Checkout Extensibility pixel

It’s recommended that most customers use the pixel’s default configuration. For more advanced configuration and customization, please contact support@fullstory.com to inquire about Fullstory Professional Services.

FAQs

The additional scripts option no longer exists in my shop, what should I do?
If you’ve chosen not to revert Checkout Extensibility, you no longer have the option to add additional scripts to the Thank You page. To accommodate, adjust the pixel’s existing code:

window._fs_additional_scripts_snippet = false;
window._fs_use_order_completed = true;

This configuration will ensure that the pixel does not rely on Fullstory capture being loaded as an additional script. It will also rename the checkout_completed event to “Order Completed” so that the products in the Shopify event can be sent to Fullstory. You will find a “Product Purchased” event corresponding to each item listed in products.

Can I use a tag manager to load the Fullstory snippet as opposed to the process described in the Installation section?
Yes. The pixel is designed to send events to the outer page (e.g. collection or product) even if the outer page’s capture is delayed. Once the tag manager loads Fullstory, any events received will be processed at that time.

Is it possible to determine if users see specific elements on the Checkout pages?
No, at this time element visibility in the viewport is not exposed by Shopify.

How can we determine if users apply discount codes or gift cards during checkout?
Depending on your configuration either the checkout_completed or “Order Completed” event will have discount code related information. The discountTitles_strs property contains discount code(s) or title(s) of discount(s) as found in Shopify administration. Additionally, the discountApplications_strs property will contain the allocationMethod, targetSelection, targetType, and type values provided by the checkout_completed event. Refer to the Shopify web pixels API for specifics on these properties.

Will Fullstory capture rage clicks, dead clicks, and error clicks on the Checkout pages?
No. These signals are not available on pages such as payment where capture is intentionally limited by Shopify.

Is it possible to determine when customers modify information within fields?
Yes, certain events provided by Shopify (e.g. checkout_contact_info_submitted), will include information provided by the customer. Additionally, input_changed events will indicate where customers enter information into a field; however, the pixel prevents capture of the value entered to limit the possibility of capturing personal or sensitive information. Pixel customization is required to adjust the code in order to capture these values. The format function in the pixel accepts a list of properties that should be removed. Alter the code format(event.data.element, ['value'])) to be format(event.data.element, []) instead.

Will data layer events appear as duplicates in Fullstory after the addition of the pixel?
If instrumented events using Fullstory APIs have names exactly matching those provided by Shopify, this situation is possible. Fullstory’s Enhanced E-commerce integration, however, has similar but different naming (add_product versus Shopify’s product_added_to_cart) and will not duplicate events. If duplicate events do exist, you can either edit the pixel’s code to remove the analytics.subscribe call for the offending event or archive an existing event to prevent it from being used by Fullstory end users. See the Sending custom event data into Fullstory article for details on archiving events.

Need to get in touch with us?

The Fullstory Team awaits your every question.

Ask the Community Technical Support