App integration
For Application Developers
Pipeline includes a JavaScript file you can use to add custom functionality to the theme and also help integrate Shopify applications with the theme. The file to use is custom.js and is located in the Assets folder:

How to use

By default, Pipeline does not load custom.js, it has been commented out of theme.liquid.
To activate custom.js, simply uncomment the script line in layout/theme.liquid:
The script tag is located near the very bottom of the file:
Copy that line out of the commented-out section and paste it below:
Save the changes to theme.liquid.
Next, you can add your custom application code block to the custom.js file.
We have included an IIFE function example for event listeners:
(function() {
// Below are example event listeners. They listen for theme events that Pipeline
// fires in order to make it easier for you to add customizations.
// Keep your scripts inside this IIFE function call to avoid leaking your
// variables into the global scope.
document.addEventListener('theme:variant:change', function(event) {
// You might use something like this to write a pre-order feature or a
// custom swatch feature.
var variant = event.detail.variant;
var container =;
if (variant) {
console.log('Container ———————— ↓');
console.log('Variant —————————— ↓');
// ... update some element on the page
document.addEventListener('theme:cart:change', function(event) {
var cart = event.detail.cart;
if (cart) {
console.log('Cart ———————————— ↓');
// ... update an app or a custom shipping caluclator
// Fired when page loads to update header values
document.addEventListener('theme:cart:init', (e) => {
// Debounced scroll listeners. Up and down only fire on direction changes
// These events are useful for creating sticky elements and popups.
document.addEventListener('theme:scroll', e => { console.log(e); });
document.addEventListener('theme:scroll:up', e => { console.log(e); });
document.addEventListener('theme:scroll:down', e => { console.log(e); });
// Debounced resize listener to bundle changes that trigger document reflow
document.addEventListener('theme:resize', e => { console.log(e); });
// Locks and unlocks page scroll for modals and drawers
// These are commented out because firing them will lock the page scroll
// the lock event must set `detail` to the modal or drawer body so the
// scroll locking code knows what element should maintain scoll.
// document.dispatchEvent(new CustomEvent('theme:scroll:lock', {bubbles: true, detail: scrollableInnerElement}));
// document.dispatchEvent(new CustomEvent('theme:scroll:unlock', {bubbles: true}));
// ^^ Keep your scripts inside this IIFE function call to avoid leaking your
// variables into the global scope.

Use case example

An example use case for adding a "Sell incoming inventory" checkbox to the product detail page:
Shopify includes a limited set of fields at the theme level. We have the ability for each variant to get:
  • Available inventory
  • Incoming inventory (from transfers)
  • Incoming inventory date (also from transfers)
A missing Shopify feature is a checkbox or ability to "Sell incoming inventory". We could say "sell unlimited quantities" or "sell what's available". However, we are unable to say "sell what's available plus the incoming and no more"
Because we are missing that feature, we can write a JavaScript function and add it to custom.js. The feature can manually add our incoming inventory to the "available" column. The logic looks like this:
  • If variant not available => "sold out"
  • If available and also incoming => "pre-order + date"
  • If available and NO incoming => add to cart
We are collapsing "available" and "incoming" together, thus we can only use transfers to track items that will be to going to pre-sell. Otherwise, anything with an incoming shipment will say "preorder".


We can add the code logic into custom.js
* The pre-order logic looks like this:
* If variant sold out and there is an incoming transfer
* Show the date of the incoming transfer
* code in snippets/add-to-cart and theme.css
* tagged pre-order
(function() {
document.addEventListener('variant:changed', function(event) {
var variant = event.detail.variant;
var container =;
var incomingJSON = container.querySelector('[data-incoming]');
if (variant) {
var incomingArray = null;
if (typeof incomingJSON !== 'undefined') {
incomingArray = JSON.parse(incomingJSON.innerText);
var arr = incomingArray.variants;
for (let index = 0; index < arr.length; index++) {
var loopVariant = arr[index];
if ( === {
variant.incoming = loopVariant.incoming;
variant.next_incoming_date = loopVariant.next_incoming_date;
if (!variant.available && variant.incoming) {
// For true pre-order you will want variant.avialable set to true
var date = new Date(variant.next_incoming_date);
var month = date.toLocaleString('default', { month: 'long' });
var day = date.getDate();
var dateString = '<p class="ProductForm__Inventory Text--subdued">Order now, aviablable on ' + month + ' ' + day + '</p>';
// show the incoming date
container.querySelector('[data-incoming-date]').innerHTML = dateString;
} else {
// wipe the incoming date
container.querySelector('[data-incoming-date]').innerHTML = '';


Next, we can add Liquid markup to the add-to-cart.liquid file:
{%- comment -%}
{%- endcomment -%}
<div data-incoming-date></div>
<script type="application/json" data-incoming="{{ }}">
{%- for variant in product.variants -%}
"id": "{{ | json }}",
"incoming": {{ variant.incoming }},
"next_incoming_date": "{{ variant.next_incoming_date | null }}"
}{%- unless forloop.last -%},{%- endunless -%}
{%- endfor -%}
{%- comment -%}
{%- endcomment -%}


Finally, add any CSS styles to customize the product detail page display:
* Pre-order incoming date */
.ProductForm__Incoming {
margin-top: 8px;
font-style: italic;
/* End Pre-order */