{{craftSnippets}}
Home Articles Plugins Starter theme Components on Github Gists About
  • Home
  • Articles
  • Building reactive Craft Commerce product page with Sprig plugin
Posted on Jun 10, 2022 by Piotr Pogorzelski

Building reactive Craft Commerce product page with Sprig plugin

Template on github gists
Simple product page which allows switching variants content without refreshing the whole page.
Table of contents:
  • Introduction
  • Product template
  • How does it work?

Introduction #

In this article, I will describe the basic Craft Commerce product template using Sprig plugin. Thanks to Sprig, users will be able to switch variants of products without refreshing the whole page - Sprig will just re-render the portion of the template that needs to be replaced server-side and inject it into the page using AJAX.

Please note that this article assumes that readers know about basic Craft Commerce functionality related to products, variants and adding products to the cart.

Product template #

Here's the template code. It is very minimalistic - it just displays the product title, current variant, "add to cart" button and variant switcher buttons.

First, root product template which itself includes Sprig component. Because of Sprig limitations, we pass only product id to the component instead of the whole product object.

{% extends 'some-template' %}
{% block content %}
	{{ sprig('product-content', {productId: product.id}) }}
	{{ sprig.script }}
{% endblock %}

Now, the template which contains the Sprig component - product-content.twig. It will be re-rendered server-side each time user switches variants and injected into the browser with AJAX request.

{% set variantParam = 'variant' %}

{# set product #}
{% set product = craft.products.id(productId).one() %}

{# set variant #}
{% set variant = product.defaultVariant %}

{# change variant if url param is present #}
{% if product.variants|filter(v => v.id == craft.app.request.getParam(variantParam)) is not empty %}
	{% set variant = product.variants|filter(v => v.id == craft.app.request.getParam(variantParam))|first %}
{% endif %}


{# product content #}
<h1>{{product.title}}</h1>

{# reactive component starts #}
<div id="sprig">

	{# show price and sku #}
	Variant: {{variant.sku}} - {{variant.price|commerceCurrency}}

	{# basket #}
	<form action="" method="post" >
		{{ actionInput('commerce/cart/update-cart') }}
		{{ csrfInput() }}
		{{ hiddenInput('purchasableId', variant.id) }}
		<button>Add to cart</button>
	</form>

	{# variant switcher #}
	{% if product.variants|length > 1 %}
		<div class="variant-widget" s-include="this">
		{% for option in product.variants %}
			<button 
				class="{{variant.id == option.id ? 'is-selected'}}"
				sprig 
				s-push-url="?{{variantParam ~ '=' ~ option.id}}" 
				s-replace="#sprig" 
				s-val:{{variantParam}}="{{ option.id }}"
				s-indicator="#preloader"
				s-method="post"
			>
				{{option.sku}}
			</button>
		{% endfor %}
		</div>
	{% endif %}

	{# preloader #}
	<div id="preloader">
		Loading
	</div>

</div>

{# show/hide preloader #}
<style>
	#preloader {
		display: none;
	}
	#preloader.htmx-request {
		display: block;
	}
</style>

{% js %}
document.body.addEventListener('htmx:afterSwap', function(evt) {
  // reinitialize js scripts
});
{% endjs %}

How does it work? #

First, we need to obtain product object based on the product id passed to the template and decide which variant should initially be displayed. It will be either the default variant, or if variant URL parameter is present - variant based on variant ID present in this parameter.

Next, we display the product title and open element which will contain content which will be replaced - <div id="sprig">. Title is placed outside this element because we assume it will never be changed. By limiting content that needs to be re-rendered, we can limit the time out requests will be processed.

Inside Sprig component we display the current variant SKU, price and add to cart form. For contains id of the current variant.

The most important part of our template is the variant switcher. It generates buttons for each variant - clicking one will replace the content of the product page with content belonging to a specific variant. Sprig will also use history API to switch variant URL param, so the browser back button will work correctly and take us to the previous variant.

Each switcher button can have is-selected class which can be used to visually distinguish button of current variant. It also has many attributes specific for Sprig that govern components behavior and what happens when we click a button:

  • s-push-url - will update URL of page with ID of changed variant
  • s-replace - defined which part of template shoudl be replaced - it contains DOM selector, in our case it is #sprig
  • s-val - defines URL params which should be provided to re-rendered component - here we update variant ID.
  • s-indicator - contains DOM selector of preloader element. In our very minimalistic styling, we just hide preloader by default and show it when sprig adds htmx-request class to it during request duration.

Please also note that the element containing all buttons has s-include="this" attribute - it will ensure that only content inside it will be treated as Sprig component. Without it, sprig would automatically also treat "add to cart" as sprig component which would cause errors.

One more thing of note is JS code at the end of the template. Because each Sprig request just swaps parts of DOM tree, all events and initialized javascript widgets will stop working. So with each request, you need re-run your JS using htmx:afterSwap event.


TAGS:
#templates #craft commerce
If you want to get latest updates on Craft CMS tutorials and components, follow me on Twitter or subscribe to RSS feed.
Quick links for this article:
Template on github gists
Articles on blog:
  • Building reactive Craft Commerce product page with Sprig plugin
  • Dynamically generated PDF attachments for Freeform submissions
  • Using template hooks in Craft CMS
  • Alpine JS modal component for Craft CMS
  • Using template UI elements to extend Craft CMS control panel
  • Matrix within a Matrix - possible solutions for Craft CMS
  • Universal email template for Craft CMS
  • Creating attributes table from entry fields in Craft CMS
  • Namespacing forms in Craft CMS
  • Creating map-based navigation for Craft CMS
  • Placeholder image macro for Craft CMS
  • Building AJAX contact form with Craft CMS
  • Using incognito field plugin for Craft CMS
  • Email footer creator made with Craft CMS
  • Infinite scrolling and lazy loading with Craft CMS
  • Using Javascript in Twig templates with Craft CMS
  • Twig templating tips and tricks for Craft CMS
  • Basic SEO functionality for Craft CMS
  • Working with dates in Craft CMS templates
  • Working with SVG images in Craft CMS templates
  • Responsive and lazy-loaded youtube videos with Craft CMS
  • Debugging and inspecting Twig templates in Craft CMS
  • Creating article excerpts with Twig component in Craft CMS
  • Adding favicons to Craft CMS website
  • Truncating text with Twig macros in Craft CMS
  • Universal language switcher for Craft CMS
  • Read time macro for Craft CMS
  • Using attr() function to render HTML attributes in Craft CMS
  • Building dynamic, AJAX based pagination for Craft CMS
  • How to add Disqus comments to Craft CMS website
  • Ellipsis pagination component for Craft CMS
  • Converting email addresses into links using Twig macro
  • Breadcrumb created from URL for Craft CMS
  • Best developer-oriented Craft CMS plugins
  • Search autocomplete component for Craft CMS
  • RSS feed - template component for Craft CMS
  • Testing emails sent by Craft CMS using Mailtrap
  • Quick edit link - Twig component for Craft CMS
  • Filtering entries in control panel using Searchit plugin
  • Fetching routes into Twig templates in Craft CMS


Dynamically generated PDF attachments for Freeform submissions

Using template hooks in Craft CMS

Alpine JS modal component for Craft CMS

Using template UI elements to extend Craft CMS control panel

Matrix within a Matrix - possible solutions for Craft CMS

Universal email template for Craft CMS

Creating attributes table from entry fields in Craft CMS

Namespacing forms in Craft CMS

Copyright ©2022 Piotr Pogorzelski