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.
;-)
— Evgeniy Mukhamedjanov, dev
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.11.2.js" | asset_url }}';
</script>
Upload the liquid-ajax-cart-v1.11.2.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.11.2.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.11.2.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:
cartRequestGet
— sends a request to theGET /cart.js
endpoint;cartRequestAdd
— to thePOST /cart/add.js
endpoint;cartRequestChange
— to thePOST /cart/change.js
endpoint;cartRequestUpdate
— to thePOST /cart/update.js
endpoint;cartRequestClear
— to thePOST /cart/clear.js
endpoint.
<script type="module">
import { cartRequestAdd } from '{{ "liquid-ajax-cart-v1.11.2.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.11.2.js" | asset_url }}'
const initialState = getCartState();
console.log('Initial state: ', initialState);
subscribeToCartStateUpdate( state => {
console.log('Updated state: ', state);
});
</script>