Guide

Liquid Ajax Cart turns Shopify “Add to cart” form submissions into Ajax requests and updates cart sections using Bundled Section Rendering.

It lets developers build Shopify Ajax carts using plain Liquid templates.

Liquid Ajax Cart doesn't apply any CSS styles.

Liquid Ajax Cart is not free.

It is distributed in exchange for GitHub stars!

A star must be sent as soon as you like the project.
Your star will be used to promote the project as it is new.

We provide dedicated support for the stargazers by email and on GitHub discussions.

— Evgeniy Mukhamedjanov, dev Evgeniy Mukhamedjanov's photo


Installation

Manual
{% comment %} Somewhere in layout/theme.liquid {% endcomment %}
 
<script type="application/json" data-ajax-cart-initial-state >
  {{ cart | json }}
</script>
 
<script type="module">
  import '{{ "liquid-ajax-cart-v1.10.1.js" | asset_url }}';
</script>

Upload the liquid-ajax-cart-v1.10.1.js file to your Shopify theme’s asset folder and include it in the layout/theme.liquid template.

Provide the initial cart state in the JSON format within a script tag with the data-ajax-cart-initial-state attribute. If not — Liquid Ajax Cart will make another Shopify Cart API request to get the cart state.

Via npm
npm install liquid-ajax-cart
// Your JavaScript module
import 'liquid-ajax-cart';
{% comment %} Somewhere in layout/theme.liquid {% endcomment %}

<script type="application/json" data-ajax-cart-initial-state >
  {{ cart | json }}
</script>

Import the liquid-ajax-cart package to a JavaScript module that is going to be processed by Webpack or any other bundler.

Provide the initial cart state in the JSON format within a script tag with the data-ajax-cart-initial-state attribute. If not — Liquid Ajax Cart will make another Shopify Cart API request to get the cart state.


Create an Ajax cart section

Feel free to use any Liquid tags, objects and filters.

Add the data-ajax-cart-section attribute to any HTML container — Liquid Ajax Cart reloads HTML of the containers every time when the Shopify cart gets updated.

Buttons

Use links to {{ routes.cart_change_url }} for “Plus”, “Minus” and “Remove” buttons. Add the data-ajax-cart-request-button to ajaxify them.

Quantity inputs

Add the data-ajax-cart-quantity-input attribute to an input element that displays a cart item’s quantity to ajaxify it.

Property inputs

Add the data-ajax-cart-property-input attribute to a line item property input to ajaxify it. The attribute also supports checkboxes, radio buttons, select and textarea tags.

The data-ajax-cart-property-input attribute works with the Shopify cart note and cart attributes as well.

Error messages

Use a container with the data-ajax-cart-messages attribute to show cart item’s error messages.

Scrollable areas

If you have a scrollable area within the section, add the data-ajax-cart-section-scroll attribute to the area — Liquid Ajax Cart keeps the scroll position unchanged while updating sections’ HTML.

Immutable containers

If you want to have an immutable HTML element, for example for a third-party app — add the data-ajax-cart-static-element attribute to this element. HTML of an immutable container will not be replaced when its section gets updated.

<!-- sections/my-cart.liquid -->

<form action="{{ routes.cart_url }}" method="post" class="my-cart">
  <!-- data-ajax-cart-section makes the element's HTML update
  when the cart gets changed -->
  <div data-ajax-cart-section>
    <h2>Cart</h2>
    
    <!-- data-ajax-cart-section-scroll keeps the scroll position unchanged
    when the element's HTML is changed -->
    <div class="my-cart__items" data-ajax-cart-section-scroll>
      {% for item in cart.items %}
        {% assign item_index = forloop.index %}
        <hr />  
        <div><a href="{{ item.url }}">{{ item.title }}</a></div>

        {% for property in item.properties %}
          <div>
            {{ property.first }}:
            {% if property.first == 'Engraving' %}
              <!-- data-ajax-cart-property-input ajaxifies the line item property input -->
              <input type="text"
                value="{{ property.last }}" 
                data-ajax-cart-property-input="{{ item_index }}[{{ property.first }}]"/>
              {% else %}
                {{ property.last }}
              {% endif %}
          </div>
        {% endfor %}

        <div>Price: {{ item.final_price | money }}</div>

        <div>
          Quantity:
          <!-- data-ajax-cart-request-button ajaxifies the "Minus one" button -->
          <a data-ajax-cart-request-button
            href="{{ routes.cart_change_url }}?line={{ item_index }}&quantity={{ item.quantity | minus: 1 }}" > 
            Minus one 
          </a>

          <!-- data-ajax-cart-quantity-input ajaxifies the line item quantity input -->
          <input data-ajax-cart-quantity-input="{{ item_index }}"
            name="updates[]" 
            value="{{ item.quantity }}" 
            type="number" />

          <!-- data-ajax-cart-request-button ajaxifies the "Plus one" button -->
          <a data-ajax-cart-request-button 
            href="{{ routes.cart_change_url }}?line={{ item_index }}&quantity={{ item.quantity | plus: 1 }}"> 
            Plus one 
          </a>
        </div>

        <!-- Container for errors -->
        <div data-ajax-cart-messages="{{ item.key }}"></div>

        <div>Total: <strong>{{ item.final_line_price | money }}</strong></div>
      {% endfor %}
    </div>

    <!-- data-ajax-cart-property-input ajaxifies the cart note textarea -->
    <textarea data-ajax-cart-property-input
      name="note" 
      placeholder="Cart note">
      {{ cart.note }}
    </textarea>
    
    <!-- data-ajax-cart-static-element keeps the container unchanged
    when the cart and the surrounding HTML get updated -->
    <div data-ajax-cart-static-element class="my-cart__app-container"></div>

    <button type="submit" name="checkout">
      Checkout — {{ cart.total_price | money_with_currency }}
    </button> 
  </div>
</form>

{% schema %} { "name": "My Cart" } {% endschema %}

Include the section to the place where you want to display the Shopify Ajax cart.

{% comment %} Most likely somewhere in theme.liquid {% endcomment %}
{% section 'my-cart' %}
Loading state

Buttons, quantity and property inputs become inactive when the cart is getting updated.

Liquid Ajax Cart adds the js-ajax-cart-request-in-progress CSS class to the body tag during the updating process so you can show a loading indicator or make the controls visually disabled.

.my-cart__items { 
  opacity: 1;
  transition: opacity .2s;
}
 
/* Make the area with controls visually disabled */
body.js-ajax-cart-request-in-progress .my-cart__items {
  opacity: .7;
}
JavaScript callback

If you want to run your JavaScript code after a Shopify Ajax cart section is updated — use the subscribeToCartSectionsUpdate function.

<script type="module">
  import { subscribeToCartSectionsUpdate } from '{{ "liquid-ajax-cart-v1.10.1.js" | asset_url }}'

  subscribeToCartSectionsUpdate( sections => {
    console.log('Sections are updated: ', sections);
    // Place your code here
  });
</script>

Enhance product forms

Liquid Ajax Cart ajaxifies all Shopify product forms.
If you want to exclude some of them — use the productFormsFilter configuration parameter.

Error messages

Add the data-ajax-cart-messages attribute with the form value to a container within product forms to show error messages.

{% form 'product', product %}
  <!-- form content -->

  <input type="submit" value="Add to cart">
  <div data-ajax-cart-messages="form" > 
    <!-- Errors and messages appear here --> 
  </div>
{% endform %}
Loading state

When a user submits a product form, it becomes and remains inactive until the Ajax “Add to cart” request is finished to prevent accidental double submissions.

Liquid Ajax Cart adds the js-ajax-cart-form-in-progress CSS class to the form if the request is in progress so you can show a loading indicator or make the submit button visually disabled.

/* Make the submit button visually disabled */
form.js-ajax-cart-form-in-progress [type="submit"] {
  opacity: .7;  
}

/* Show a loading indicator */
form.js-ajax-cart-form-in-progress:after { 
  content: 'Adding to cart…'
  display: block; 
}

Show/hide a cart section on a button click

Write some CSS to show your Shopify Ajax cart section if a specific body CSS class exists.

<!-- Floating cart -->
<div class="my-floating-cart"> {% section 'my-cart' %} </div>

<style>
  .my-floating-cart { display: none; }

  /* Show the floating cart if the 'js-my-cart-open' CSS class exists */
  .js-my-cart-open .my-floating-cart { display: block; }
</style>

Add the data-ajax-cart-toggle-class-button attribute with the CSS class as a value to the button that have to show/hide the cart — the button will add/remove the CSS class on a user’s click.

<!-- Button to open/close the floating cart -->
<a href="{{ routes.cart_url }}"
  data-ajax-cart-toggle-class-button="js-my-cart-open">
  My Cart
</a>

If you want the button to only add or only remove the CSS class — specify the additional parameter.

<button data-ajax-cart-toggle-class-button="js-my-cart-open | add">
  Open cart
</button>

<button data-ajax-cart-toggle-class-button="js-my-cart-open | remove">
  Close cart
</button>

Show a cart section after adding a product to the cart

Write some CSS to show your Shopify Ajax cart section if a specific body CSS class exists.

<!-- Floating cart -->
<div class="my-floating-cart"> {% section 'my-cart' %} </div>

<style>
  .my-floating-cart { display: none; }

  /* Show the floating cart if the 'js-my-cart-open' CSS class exists */
  .js-my-cart-open .my-floating-cart { display: block; }
</style>

Use the addToCartCssClass configuration parameter to specify the CSS class that should be attached to the body tag after successful adding a product to the cart.

The parameter also lets you define the time after which the class should be removed.

{% comment %} Somewhere in layout/theme.liquid {% endcomment %}

<script type="application/json" data-ajax-cart-configuration >
  {
    "addToCartCssClass": "js-my-cart-open"
  }
</script>

Display Shopify cart’s total price and items counter outside of Ajax cart sections

Add the data-ajax-cart-bind-state attribute to an HTML element, pass a Cart state property as an attribute’s value and Liquid Ajax Cart will display the state property’s value within the HTML element and refresh it when cart gets updated.

Explore all the properties on the State reference page.

<div data-ajax-cart-bind-state="cart.item_count" > 
  <!-- Cart item count appears here --> 
</div>

<!-- The best practice is to use [data-ajax-cart-bind-state] on top of Liquid expressions.
The liquid expression {{ cart.item_count }} displays the amount 
even if Liquid Ajax Cart is not loaded: -->
<div data-ajax-cart-bind-state="cart.item_count" >
  {{ cart.item_count }}
</div>

<!-- Use the "money_with_currency" formatter
to show money related values -->
<div data-ajax-cart-bind-state="cart.total_price | money_with_currency">
  {{ cart.total_price | money_with_currency }}
</div>

Run your Javascript code before and after each Shopify Cart API request

Use the subscribeToCartAjaxRequests function to add callbacks that will be called before and after Shopify Cart API requests.

<script type="module">
  import { subscribeToCartAjaxRequests } from '{{ "liquid-ajax-cart-v1.10.1.js" | asset_url }}'

  subscribeToCartAjaxRequests(( requestState, subscribeToResult ) => {    
    // This function will be called before each Shopify Cart API request
    console.log( 'Before: ', requestState );
 
    subscribeToResult( requestState => {   
      // This function will be called after the request is finished
      console.log( 'After: ', requestState );
    })
  })
</script>

Make your own calls to Shopify Ajax Cart API

Use these function instead of direct Cart API calls and Liquid Ajax Cart will keep the Ajax cart sections, the State object and body CSS classes updated:

<script type="module">
  import { cartRequestAdd } from '{{ "liquid-ajax-cart-v1.10.1.js" | asset_url }}'

  cartRequestAdd({ 
    items: [
      {
        id: 40934235668668,
        quantity: 1
      }
    ]  
  });
</script>

Get Cart State JSON

State is a Javascript object where Liquid Ajax Cart keeps the information related to the user’s cart.

{
  "cart":{
    "token":"b7d3743e2c398043f209c5a3a9014f9d",
    "note":null,
    "attributes":{},
    "original_total_price":1000,
    "total_price":1000,
    "total_discount":0,
    "total_weight":0,
    "item_count":1,
    "items":[{}],
    "requires_shipping":false,
    "currency":"USD",
    "items_subtotal_price":1000,
    "cart_level_discount_applications":[]
  },
  "previousCart":{},
  "status":{
    "requestInProgress":false,
    "cartStateSet":true
  }
}

Use the getCartState function to get the current state.

If you want to run your JavaScript code every time when the state is changed — use the subscribeToCartStateUpdate function to subscribe to state updates.

<script type="module">
  import { getCartState, subscribeToCartStateUpdate } from '{{ "liquid-ajax-cart-v1.10.1.js" | asset_url }}'

  const initialState = getCartState();
  console.log('Initial state: ', initialState);

  subscribeToCartStateUpdate( state => {
    console.log('Updated state: ', state);
  });
</script>