Taking Ajax to SPA level in Shopify

Have you ever tried updating a page via Ajax in Shopify? It can feel like your hands are tied. Sometimes it’s awkward or simply not possible to get the data you need because Shopify’s Ajax API is somewhat limited. I was working on a site that had some pretty demanding requirements and at first I didn’t think we’d be able to meet them, or if we did it would require an expensive custom app. But I came up with a technique that turned out to be a bit of a game changer, and we’ve used it over and over again. It’s possible this is a well-known technique that is commonly used, but I have never seen it mentioned anywhere before. 😅

The secret is this: you can use a parameter in a product URL to specify a display template.

If you’re still on your chair let me explain with a few real-world examples from the site I was working on.

Search results

The site has a product lookup where customers put in some vehicle information and get a list of products that fit their vehicle. Behind the scenes this is powered by an embedded Vue app which talks to an external Laravel app. We wanted the styling and layout of search results to be kept in the Shopify theme to make things more consistent and easier to edit. So the Vue app handles the search controls and fetching results, but then references a Shopify template for displaying results. Instead of having a markup template in the Vue app, it fetches the markup for each result from a Shopify URL: https://www.shop-domain.com/product-handle?view=search-result. That last part — view=search-result — is the secret sauce. It tells Shopify to display the product using the product.search-result.liquid template, and that template is built to display only what’s needed for a search result.

This allows for separation of concerns. Laravel handles data, Vue handles interaction, and the store theme handles display. If we want to change how search results are displayed, we can do that without touching Vue or Laravel, which in this case are managed by other teams.

Cart functionality

This technique simplified two cart-related functions:

  1. Ajax-powered add-to-cart. When a user adds something to the cart, a cart panel slides out with a product tile showing what they just added to their cart.
  2. Automatic add-ons. This store runs promotions where customers get a free gift if their order is above a certain dollar amount. If the customer increases their order amount and meets the minimum order value while on the cart page (by increasing the quantity of a line item) we automatically add the free gift to their cart.

In both cases, the product tile is added to the page via Ajax, which simply fetches the entire markup for the tile from a product URL with the view parameter. This simplifies things all around. The JavaScript is simpler because all it does is fetch an HTML blob and insert it into the page. And templating is cleaner because you’re working with a Liquid template instead of HTML in JS.

Switching between products

This store has product “families” which consist of a handful of SKUs that are effectively the same product with different finishes. Normally this would be implemented as variants of a single product, but for reasons I won’t get into here it was decided that each SKU should be a separate product in Shopify. When viewing a product page, the other products in the family are displayed in a grid and there are buttons for switching to one of the other products. Selecting a product updates the page content (specs, features, images) to match that product, without reloading the page.

Normally this would be done via Ajax by fetching the product data and plugging it into all the right places in the DOM. Instead, we created a product template that consists of only the page body (no header and footer). When someone selects a different product in the family, a bit of JS fetches the HTML for that product page (with view=page-content-only) and simply replaces the entire page. As you can imagine this greatly simplifies the JavaScript. On top of that, maintenance is much easier because there’s no template mirroring. If the product page design/markup changes, there’s no need to update the JavaScript to match because all it does is grab some HTML and replace everything in <main>. And with Liquid snippets, the exact same template that’s used for initially loading a product page is also used when replacing the page content, so there’s only one template to maintain.

Conclusion

This technique of using a parameter in a product URL to specify a display template can be a game changer for Shopify developers who struggle with the limited capabilities of Shopify’s Ajax API. By fetching markup for product tiles and page content from Shopify URLs with the view parameter, developers can simplify their JavaScript, keep the display consistent with the theme, and separate concerns between data, interaction, and display. This technique has been used successfully for search results, cart functionality, and switching between products, and may be applicable to other use cases as well.