Code Snippets
Snippets in Shopify are extremely useful and allow you to keep repeated code in a single file.
For version 2 themes, snippets shouldn't be required, but for version 1 themes it is usually sufficient to use spiff-button-standard
.
snippets/spiff-button-standard.liquid
This snippet of code will generate a button on a webpage that, when clicked, will launch the Spiff workflow experience. Based on the customer's selections during the workflow, the experience will return a design or standard product.
A standard product will be returned if there are no price modifications based on variant selections. The logic will directly put the base product in the cart.
A design product is when there are price modifications based off of variant selections. When the price of the product is changed we can no longer insert the base product to the cart. To combat this we create a new product in your store with the new price. These products will be marked under Spiff Order Product
.
<script type="text/javascript" async src="https://assets.us.spiffcommerce.com/shopify.js"></script> <!-- Non-US users also use the US URL. -->
{% assign product_object = all_products[product_handle] %}
<style>
.spiff-spinner-outer {
animation: rotator 1s linear infinite;
}
.spiff-spinner-inner {
stroke-dasharray: 45 180;
}
@keyframes rotator {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
<script>
var pageSessionId;
var openStandardWorkflowForProduct{{product_object.id}};
const go = function() {
const product = new window.Spiff.Product({
integrationId: '{{shop.permanent_domain}}',
productId: '{{product_object.id}}'
});
const hideElements = () => {
const elements = document.querySelectorAll('[data-spiff-hide]');
elements.forEach(el => {
const externalProductId = el.getAttribute('data-product-id');
if (externalProductId === '{{product_object.id}}') {
el.style.display = 'none';
}
});
}
product.on('ready', () => {
hideElements();
if (pageSessionId === undefined) {
pageSessionId = window.Spiff.Analytics.createPageSession();
}
const buttons = document.querySelectorAll('.spiff-api-button[data-product-id="{{product_object.id}}"]');
for (button of buttons) {
button.style.display = 'block';
}
});
product.confirmActive();
openStandardWorkflowForProduct{{product_object.id}} = (event) => {
event.target.disabled = true;
event.preventDefault();
event.stopPropagation();
const spinnerSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
spinnerSvg.setAttribute('class', 'spiff-spinner-outer');
spinnerSvg.setAttribute('height', event.target.clientHeight);
spinnerSvg.setAttribute('width', event.target.clientHeight);
spinnerSvg.setAttribute('viewBox', '0 0 66 66');
const spinnerCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
spinnerCircle.setAttribute('class', 'spiff-spinner-inner');
spinnerCircle.setAttribute('cx', 33);
spinnerCircle.setAttribute('cy', 33);
spinnerCircle.setAttribute('fill', 'none');
spinnerCircle.setAttribute('r', 30);
spinnerCircle.setAttribute('stroke', 'black');
spinnerCircle.setAttribute('stroke-width', 6);
spinnerSvg.appendChild(spinnerCircle);
event.target.appendChild(spinnerSvg);
const transactionOptions = {
presentmentCurrency: '{{cart.currency.iso_code}}',
product: product,
shouldCreateDesignProduct: false,
pageSessionId
};
const transaction = new window.Spiff.Transaction(transactionOptions);
transaction.on('quit', () => {
event.target.removeChild(spinnerSvg);
event.target.disabled = false;
console.log("The user exited before completing their design");
});
transaction.on('complete', async (result) => {
const lineItemProperties = {
spiffTransactionId: result.transactionId,
previewImage: result.previewImage
};
const keys = Object.keys(result.exportedData);
for (const key of keys) {
lineItemProperties[key] = result.exportedData[key].value;
}
const firstVariantId = '{{product_object.variants[0].id}}';
const addToCartRequestBody = JSON.stringify({
quantity: 1,
id: firstVariantId,
properties: lineItemProperties
});
await fetch('/cart/add.js', {
method: 'POST',
body: addToCartRequestBody,
headers: { 'Content-Type': 'application/json' }
});
event.target.removeChild(spinnerSvg);
event.target.disabled = false;
{% if redirect_to_cart %}window.top.location.href = '/cart';{% endif %}
});
transaction.execute();
};
}
if (window.Spiff) {
go();
} else {
window.addEventListener('SpiffApiReady', go);
}
</script>
<button
class='spiff-api-button'
data-product-id='{{product_object.id}}'
onclick="openStandardWorkflowForProduct{{product_object.id}}(event)"
style="
border-radius:0.5rem;
display:none;
color:{% if label_colour %}
{{label_colour}}
{% else %}
#FFFFFF
{% endif %};
background-color:{% if background_colour %}
{{background_colour}}
{% else %}
rgb(214, 27, 92)
{% endif %};
{% if height %}height:{{height}};{% endif %}
{% if width %}width:{{width}};{% endif %}
"
>{% if label_text %}{{label_text}}{% else %}Click to Create{% endif %}</button>
Example
{% include 'spiff-button-standard' product_handle: product.handle, redirect_to_cart: true %}
snippets/spiff-button-design-product.liquid
This snippet is generally only needed in cases where custom pricing is involved.
<script type="text/javascript" async src="https://assets.us.spiffcommerce.com/shopify.js"></script> <!-- Non-US users also use the US URL. -->
{% assign product_object = all_products[product_handle] %}
<style>
.spiff-spinner-outer {
animation: rotator 1s linear infinite;
}
.spiff-spinner-inner {
stroke-dasharray: 45 180;
}
@keyframes rotator {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
<script>
var openDisplayProductWorkflowForProduct{{product_object.id}};
var pageSessionId;
const go = function() {
const product = new window.Spiff.Product({
integrationId: '{{shop.permanent_domain}}',
productId: '{{product_object.id}}'
});
const hideElements = () => {
const elements = document.querySelectorAll('[data-spiff-hide]');
elements.forEach(el => {
const externalProductId = el.getAttribute('data-product-id');
if (externalProductId === '{{product_object.id}}') {
el.style.display = 'none';
}
});
}
product.on('ready', () => {
hideElements();
const buttons = document.querySelectorAll('.spiff-api-button[data-product-id="{{product_object.id}}"]');
if (pageSessionId === undefined) {
pageSessionId = window.Spiff.Analytics.createPageSession();
}
for (button of buttons) {
button.style.display = 'block';
}
});
product.confirmActive();
openDisplayProductWorkflowForProduct{{product_object.id}} = (event) => {
event.target.disabled = true;
event.preventDefault();
event.stopPropagation();
const spinnerSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
spinnerSvg.setAttribute('class', 'spiff-spinner-outer');
spinnerSvg.setAttribute('height', event.target.clientHeight);
spinnerSvg.setAttribute('width', event.target.clientHeight);
spinnerSvg.setAttribute('viewBox', '0 0 66 66');
const spinnerCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
spinnerCircle.setAttribute('class', 'spiff-spinner-inner');
spinnerCircle.setAttribute('cx', 33);
spinnerCircle.setAttribute('cy', 33);
spinnerCircle.setAttribute('fill', 'none');
spinnerCircle.setAttribute('r', 30);
spinnerCircle.setAttribute('stroke', 'black');
spinnerCircle.setAttribute('stroke-width', 6);
spinnerSvg.appendChild(spinnerCircle);
event.target.appendChild(spinnerSvg);
const transactionOptions = {
presentmentCurrency: '{{cart.currency.iso_code}}',
product: product,
shouldCreateDesignProduct: true,
pageSessionId
};
const transaction = new window.Spiff.Transaction(transactionOptions);
transaction.on('quit', () => {
event.target.removeChild(spinnerSvg);
event.target.disabled = false;
console.log("The user exited before completing their design");
});
transaction.on('complete', async (result) => {
const lineItemProperties = {
spiffTransactionId: result.transactionId,
previewImage: result.previewImage
};
const keys = Object.keys(result.exportedData);
for (const key of keys) {
lineItemProperties[key] = result.exportedData[key].value;
}
const addToCartRequestBody = JSON.stringify({
quantity: 1,
id: result.designProductVariantId,
properties: lineItemProperties
});
await fetch('/cart/add.js', {
method: 'POST',
body: addToCartRequestBody,
headers: { 'Content-Type': 'application/json' }
});
event.target.removeChild(spinnerSvg);
event.target.disabled = false;
{% if redirect_to_cart %}window.top.location.href = '/cart';{% endif %}
});
transaction.execute();
};
};
if (window.Spiff) {
go();
} else {
window.addEventListener('SpiffApiReady', go);
}
</script>
<button
class='spiff-api-button'
data-product-id='{{product_object.id}}'
onclick="openDisplayProductWorkflowForProduct{{product_object.id}}(event)"
style="
display:none;
color:{% if label_colour %}
{{label_colour}}
{% else %}
#FFFFFF
{% endif %};
background-color:{% if background_colour %}
{{background_colour}}
{% else %}
rgb(214, 27, 92)
{% endif %};
{% if height %}height:{{height}};{% endif %}
{% if width %}width:{{width}};{% endif %}
"
>{% if label_text %}{{label_text}}{% else %}Customise now on #spiff{% endif %}</button>
snippets/spiff-cart-item-edit.liquid
The edit button can be used to return a customer to our experience and modify an existing design. This can be inserted onto the cart line item template. Create a new snippet and paste the following code into it to create an edit button. This should be rendered per line_item in your cart and will render the edit button when a spiffTransactionId is present.
{% for p in item.properties %}
{% if p.first contains 'spiffTransactionId' %}
{% assign spiffTransactionId = p.last %}
{% endif %}
{% endfor %}
{% assign filteredSpiffTrans = spiffTransactionId | replace: '-', '' %}
<script defer async type="text/javascript" src="https://assets.us.spiffcommerce.com/shopify.js"></script> <!-- Non-US users also use the US URL. -->
<script>
var onEdit{{filteredSpiffTrans}};
function attachEditButton() {
onEdit{{filteredSpiffTrans}} = (event) => {
event.target.disabled = true;
event.preventDefault();
event.stopPropagation();
const transactionOptions = {
transactionId: '{{ spiffTransactionId }}'
};
const transaction = new window.Spiff.Transaction(transactionOptions);
transaction.on('quit', () => {
event.target.disabled = false;
});
transaction.on('complete', async (result) => {
const lineItemProperties = {
_spiffTransactionId: result.transactionId,
previewImage: result.previewImage
};
const keys = Object.keys(result.exportedData);
for (const key of keys) {
lineItemProperties[key] = result.exportedData[key].value;
}
const response = await fetch('/cart.js');
const json = await response.json();
let property = json.items.find(i => i.properties._spiffTransactionId === '{{ spiffTransactionId }}');
if ('{{ spiffTransactionId }}' && result.transactionId === '{{ spiffTransactionId }}') {
const updateLineItemData = {
id: property.key,
quantity: 0
}
await fetch('/cart/change.js', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(updateLineItemData)
});
const lineItemData = {items: [
{
id: result.externalCartProductVariantId,
quantity: 1,
properties: lineItemProperties
}
]}
fetch('/cart/add.js', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(lineItemData)
})
.then(response => {
window.top.location.href = '/cart';
})
.catch((error) => {
console.error('Error:', error);
});
}
});
transaction.execute();
}
};
if(window.Spiff) {
attachEditButton();
} else {
window.addEventListener('SpiffApiReady', function () {
attachEditButton();
})
}
</script>
{% if spiffTransactionId %}
<button class="edit-btn" onclick="onEdit{{filteredSpiffTrans}}(event)">Edit</button>
{% endif %}
snippets/spiff-line-item-image.liquid
The line item image snippet will insert the preview image generated from the customer's final design into the cart.
{% for property in line_item.properties %}
{% if property.first == "spiffTransactionId" %}
<span class='spiff-line-item-image-container' data-spiff-transaction-id="{{ property.last }}">
</span>
{% assign hasTransactionId = true %}
{% endif %}
{% if property.first == "previewImage" %}
<img src="{{ property.last }}" alt="{{ line_item.title }}" />
{% endif %}
{% endfor %}
{% unless hasTransactionId %}
<img class="cart__image{% if line_item.image == null %} hide{% endif %}" src="{{ line_item | img_url: 'x190' }}" alt="{{ line_item.image.alt | escape }}" data-cart-item-image>
{% endunless %}