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.
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.
This technique simplified two cart-related functions:
- 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.
- 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
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
<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.
In 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