Introduction
What is Cart.js?
Cart.js is a very small open source Javascript library that makes the addition of powerful Ajax cart functionality to your Shopify theme a breeze.
It's designed to be simple to use, while providing some really powerful and nifty features, like:
- Simple, consistent API for cart manipulation;
- Data API for markup-only use without needing to write a line of Javascript;
- DOM Binding to dynamically render HTML templates as your cart changes.
You don't need to worry about ensuring your Ajax requests are synchronous, binding event listeners, or updating the DOM.
Getting Started
Getting started with Cart.js is designed to be pretty easy.
You just need to fetch a copy of the library, include it in your theme, and call a single initialisation method. Then you can make calls to Cart.js from within your theme code.
Installation
Cart.js is available via the npm
or bower
package managers, from
cdnjs, or simply by downloading the latest version of the
library.
NPM
npm install shopify-cartjs
Bower
bower install shopify-cartjs
CDNJS
<!-- Only include one of the below in your theme. -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/shopify-cartjs/1.1.0/cart.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/shopify-cartjs/1.1.0/rivets-cart.min.js"></script>
Download
Download the latest version of the library: cartjs.zip.
Setup
The Cart.js distribution comes packaged with three versions of the library:
cart.js
is the unminified source code, containing the Core and Data APIs.
cart.min.js
is a minified version of the library, and also contains the Core and Data APIs.
rivets-cart.min.js
is a minified version of Cart.js that also bundles the Rivets.js library.
Together, they provide support for the DOM Binding functionality.
Once you've selected the version you'd like to use, add the relevant file to your theme's /assets
directory.
You then just need to include the script on your page and call CartJS.init()
.
The best place to do this is at the bottom of your theme's theme.liquid
file, so that Cart.js functionality is available across your whole site.
Because Cart.js depends on jQuery, you should load it after you've included the jQuery library.
... contents of your theme.liquid ...
<!-- Include jQuery from Google's CDN. -->
<!-- Your theme may already include jQuery - if so, you can skip this line. -->
{{ '//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js' | script_tag }}
<!-- Include Cart.js -->
{{ 'cart.min.js' | asset_url | script_tag }}
<!-- Initialise Cart.js once the page has loaded -->
<script type="text/javascript">
jQuery(function() {
CartJS.init({{ cart | json }});
});
</script>
</body>
</html>
Note that the call to CartJS.init()
requires that {{ cart | json }}
is passed as an argument.
This tells Liquid to render the initial cart state as a JSON object and pass it to Cart.js.
Dependency when formatting monetary values
If you're using any of the money-formatting features of Cart.js (such as the | money
filter in the DOM Binding
module or the data-cart-render
attributes of the Data API), you'll need to make sure that Shopify's currency
Javascript library is loaded.
Do this simply by ensuring that the option_selection.js
library is loaded on all of your theme's pages, using
a line something like {{ 'option_selection.js' | shopify_asset_url | script_tag }}
in your theme.liquid
.
Next Steps
Now that you gotten Cart.js set up with your theme, you're ready to start using the library.
The recommended (and easiest) way to interact with Cart.js is via the Data API. All this requires is adding some additional markup to your HTML and Cart.js will take care of the rest - you don't need to write any additional Javascript.
If you want to do something that's not supported by the Data API, or use Cart.js from within your own custom Javascript, then you can use the Core API to call methods on the CartJS
object directly.
Of course, these two approaches aren't mutually exclusive - you can always use the Data API for the majority of your cart functionality, drop down to the Core API only when needed.
The Core API is covered first in the documentation below, as it provides the foundation for the Data API.
However, if you just want to get stuck in to using Cart.js, feel free to go straight to the coverage of the Data API.
Once you've gotten the hang of the Core and Data APIs, you might be interested in using Cart.js in conjunction with Rivets.js to create HTML templates that are automatically updated along with your cart. That's covered in the DOM Binding section later on.
Browser Support
If you're using only the Core and Data functionality of Cart.js (that is,
you're using the cart.js
or cart.min.js
library in your theme), then the
range of browsers your theme will support is limited purely by the version of
jQuery you're using. That means that using a 1.x
version of jQuery will allow
you to support IE6+, Chrome, Firefox, Safari 5.1+ and up.
If you're using the DOM Binding functionality (that is, you're using the
rivets-cart.min.js
library in your theme), then you may see problems on
browsers that don't support the ES5 Javascript standard (namely, Internet
Explorer 8 and below).
As of November 2015, Shopify no longer requires themes submitted to the theme store to support Internet Explorer 8, so as long as you don't have a special use case that requires supporting these older browsers, you can happily use the DOM Binding library in your themes.
What's the issue with older browsers?
Non-ES5 browsers (namely, IE8) don't support EcmaScript 5's
Object.defineProperty()
method, which Rivets.js uses to observe
changes on data models and trigger DOM updates.
Earlier versions of Cart.js provided a workaround for this by bundling a number of ES5 shims and polyfills into the library, as well as forcibly binding and re-binding Rivets views whenever a charge to the cart occurred. This "compatibility mode" was dropped once Shopify changed their theme guidelines to only require support for Internet Explorer 9+.
Module Loaders
If you're using Cart.js and Rivets with module loaders like Webpack, you'll need to make a couple of minor tweaks to ensure things work smoothly.
Ensure you're pulling in jQuery and Rivets in your webpack.config.js
or
equivalent:
new webpack.ProvidePlugin({ $: "jquery", rivets: "rivets", }),
and also make sure that Rivets is available globally in your entrypoint file:
global.rivets = rivets;
Core API
The Core API consists of methods called on the global CartJS
object.
You can use the Core API to do pretty much everything you'd expect of a cart manipulation library - add items, update quantities and custom properties, and so on.
A full list of methods can be found in the API Reference.
Configuration
In the "Setup" section above, we saw that we need to call CartJS.init()
before use, like this:
<script type="text/javascript">
jQuery(function() {
CartJS.init({{ cart | json }}, {
"dataAPI": false,
"requestBodyClass": "loading"
});
});
</script>
As you can see, the init()
method takes two arguments.
The first argument is required, and is provided by rendering the current Shopify cart as a JSON object through the {{ cart | json }}
Liquid tag.
The second argument is an optional hash of configuration options. A full list of these options is available in the Option Reference.
Cart State
If we want to inspect the state of our cart at any time, we can access the CartJS.cart
object.
You can read any of the standard cart properties (such as item_count
or requires_shipping
, for example), as well as a list of the current items
in the cart.
While developing with Cart.js, it's often useful to open your browser's Javascript console and inspect the cart state, or test out Cart.js methods. Here's an example from the developer console in Chrome:
Don't write values via the cart object
You should only ever read values from CartJS.cart
, and avoid altering the object directly.
Assigning a value with code like CartJS.cart.items[0].quantity = 5;
will make the change locally in the browser, but won't save it to the server.
This means that the changes will be lost when the customer refreshes or navigates to a new page, or when Cart.js fetches an updated version of the cart from Shopify.
Adding Items
Adding items to your cart is as simple as calling CartJS.addItem()
, and passing the ID of the variant you'd like to add as the first argument.
Assume we have a Shopify store that's selling widgets, and that one of those widgets has a variant with an ID of 12345678
that costs $9.99.
Let's create a button customers can click to add a widget to their cart, and then hook it up to some Javascript code via jQuery:
<button id="button">Add a Widget</button>
<script type="text/javascript">
$('#button').click(function() {
CartJS.addItem(12345678);
});
</script>
Now when a customer clicks our button, Cart.js will make an Ajax request and add a single Widget to the customer's cart.
When using the addItem()
method, you can optionally specify the quantity to add and a hash of custom line item properties.
You can also specify the special optional selling_plan
property, which will be used by Cart.js to specify the selling plan the item will be sold with.
Let's update our code to add five widgets when we click the button, to use the selling plan 1425
and to set a custom "added_by" property on the resulting line item:
<button id="button">Add Five Widgets</button>
<script type="text/javascript">
$('#button').click(function() {
CartJS.addItem(12345678, 5, {
"selling_plan": 1425,
"added_by": "Cart.js"
});
});
</script>
If we loaded this example, clicked "Add Five Widgets", then typed CartJS.cart.items
in to our browser's Javascript console, we'd see something like this (simplified for this example):
[
{
"handle": "widget-1",
"id" 12345678,
"price": 999,
"line_price": 4995,
"properties": {
"added_by": "Cart.js"
},
"quantity": 5,
"title": "Widget 1",
"variant_id": 12345678,
"selling_plan_allocation": {
"selling_plan": {
"id": 1425
}
}
}
]
That's it!
You can call addItem()
as many times as you like in the same function, and Cart.js will queue up Ajax requests as needed.
Adding multiple line items at once
If you're adding multiple line items at once, you can use the CartJS.addItems()
method, rather than multiple calls to CartJS.addItem()
.
This method leverages new functionality added to the Shopify Ajax API in January 2020 that supports multiple items being added at once.
Note on multiple line items with the same variant ID
Shopify will collate multiple line items for the same variant into one — for example, if we clicked "Add Five Widgets" in the example above again, we'd end up with one line item with "quantity": 10
instead of two line item with "quantity: 5"
.
However, this doesn't apply when you add the same variant with custom line item properties that differ — if we changed the value of the added_by
property and clicked the button, we'd end up with separate line items.
Updating Items
Updating the quantities or properties of line items is just as simple as adding them -- we just make a call to the updateItem()
method.
Let's continue from our example above, and say we want to have a button that doubles the number of widgets in our order.
<button id="button-double">Double my Order!</button>
<script type="text/javascript">
$('#button-double').click(function() {
var newQuantity = CartJS.cart.items[0].quantity * 2;
CartJS.updateItem(1, newQuantity);
});
</script>
Existing items referenced by index, not variant ID
One important thing to note is that the updateItem()
method takes the line number (the "index") of the item in the cart you'd like to update, not the variant ID.
This is because it's possible (and quite common) to have multiple items in the cart with the same variant ID but with different properties.
Shopify uses a 1-based index for line items, so the index of the first line item in a cart is 1
, not 0
as is common in many programming languages.
If you'd like to update an item using just the variant ID, you can use updateItemById()
, which operates the same way as updateItem()
but takes the variant ID as the first parameter.
Removing Items
Removing items works in a similar way to updating items -- just call the removeItem()
method, passing the line number of the line item you'd like to remove.
As with the update method, if you'd like to remove all line items with a particular variant ID, you can use removeItemById()
instead.
If you'd like to empty the cart completely, just call the clear()
method:
<button id="button-empty">Empty Cart</button>
<script type="text/javascript">
$('#button-empty').click(function() {
CartJS.clear();
});
</script>
Cart Attributes
In addition to cart items, Shopify also provides support for cart attributes, which are used to store custom information about an order. For example, a common use case for attributes is to store a flag indicating whether the customer would like their order gift wrapped.
Cart.js provides some conveniences around manipulating these attributes. Let's take the gift wrap example and see how we could implement it with a simple checkbox:
<label>
<input id="gift-wrap-checkbox" type="checkbox" />
Please gift wrap my order
</label>
<script type="text/javascript">
// Update the 'Gift Wrap' cart attribute based on the state of the checkbox whenever it changes.
$('#gift-wrap-checkbox').change(function() {
CartJS.setAttribute('Gift Wrap', this.checked ? 'Yes': 'No' );
});
</script>
Now, any time a customer checks or un-checks the options, CartJS will make an update request to the Shopify server.
Cart.js provides convenience methods for setting multiple attributes at once, clearing attributes, and setting the special-case note
attribute.
For a full list of supported methods, see the API Reference.
Callbacks
All Core API methods that result in an Ajax request being made to the server allow you to specify one or more callback functions, just like a regular jQuery $.ajax()
request.
Callbacks are specified through an options
hash, passed as the final optional argument to the core methods.
To take a common example, we'll often want to provide callback methods to handle possible results when we try to add an item to our cart. We want to inform the user of the success or failure of their action, and potentially take some other action.
<button id="button">Add to Cart</button>
<div id="message"></div>
<script type="text/javascript">
$('#button').click(function() {
// Call the addItem() method.
// Note the empty object as the third argument, representing no line item properties.
CartJS.addItem(12345678, 1, {}, {
// Define a success callback to display a success message.
"success": function(data, textStatus, jqXHR) {
$('#message').addClass('message-success');
$('#message').html('Successfully added to cart.');
},
// Define an error callback to display an error message.
"error": function(jqXHR, textStatus, errorThrown) {
$('#message').addClass('message-error');
$('#message').html('There was a problem adding to the cart!');
}
});
});
</script>
Data API
The Data API is the easiest way to start using Cart.js, and for a lot of use cases it'll be all you need.
All you need to do is add the appropriate data-
attributes to your code, and the event listeners automatically set up by Cart.js will do the rest.
This guide provides a quick overview - for a full list of available markup attributes, check out the Data API Reference.
Adding Items
To create an element that adds an item to the cart on a click
event, just mark up an element with a data-cart-add
attribute, and set the value of the attribute to the ID of the variant you'd like to add:
<button data-cart-add="12345678">Add a Widget</button>
Of course, you can always use Liquid to render these elements dynamically in your templates:
{% for variant in product.variants %}
<button data-cart-add="{{ variant.id }}">Add a {{ variant.title }}</button>
{% endfor %}
You can also optionally specify a quantity and selling plan for the add action:
<button data-cart-add="12345678" data-cart-quantity="2" data-cart-selling-plan="1425">Subscribe to 2 widgets</button>
Removing Items
To create an element that removes a line item from the cart on a click
event, use data-cart-remove
or data-cart-remove-id
.
The difference between these two is that data-cart-remove
expects the index of the line item you'd like to remove from the cart, while data-cart-remove-id
expects a variant ID.
See Updating Items in the Core API section for an explanation of why this distinction is required.
<button data-cart-remove-variant="12345678">Remove Widgets from Cart</button>
Toggling Items
If you want to give your customers the ability to "toggle" items in or out of their cart, you can use a data-cart-toggle
attribute on an element that fires a change
event (like a checkbox or radio input).
A common use case for this is having an "added extra" on the cart page before the customer checks out - for example, a gift card.
Let's say we have a selection of gift cards a customer can choose from, and that we've created a collection containing them:
{% for gift_card_product in collections.gift-cards.products %}
<label>
<input type="checkbox" data-cart-toggle="{{ gift_card_product.variants.first.id }}" />
Add {{ gift_card_product.title | escape }} to my order (+{{ gift_card_product.variants.first.price | money_with_currency }})
</label>
{% endfor %}
This code will render a checkbox that a customer can turn on or off to add or remove the desired gift card from their card.
Submitting Forms
If you've already got a working Shopify theme, you'll already have a couple of <form>
elements around the place that you use to add items to the cart - for example, in your product.liquid
template.
You can convert these existing forms to use Cart.js simply by adding a data-cart-submit
attribute to the <form>
element:
<form action="/cart/add" method="post" data-cart-submit>
<select name="id">
{% for variant in product.variants %}
<option value="{{ variant.id }}">{{ variant.title }} - {{ variant.price | money }}</option>
{% endfor %}
</select>
<button type="submit">Buy Now</button>
</form>
When the customer clicks "Buy Now", Cart.js will intercept the form submission and convert it to an Ajax request.
All of the usual inputs - id
to specify the variant ID, quantity
to specify the quantity, selling_plan
to specify the selling plan, and properties[]
to specify line item properties - are supported.
Form submission doesn't work with files
Because Ajax requests don't support POST
requests with enctype="multipart/form-data"
, Cart.js won't be able to submit your form if it contains <input type="file">
elements.
This is something that we hope to auto-detect in future, but for now you should avoid using data-cart-submit
on forms that need to upload files.
Form submission doesn't work with checkout forms
Be careful not to add the data-cart-submit
attribute to forms that target action="/checkout"
, as Cart.js will intercept the event and customers won't be redirected to the checkout.
Events
To make it easy to respond to changes in the customer's cart, Cart.js fires a number of custom jQuery events that you can bind listeners to.
All events are triggered on the document
, are prefixed with a cart
namespace, and pass the current cart object as the first custom event argument.
For example, if we wanted to update a <span>
element with the number of items in the cart after a request, we could listen for the cart.requestComplete
event like so:
You have <span id="counter">{{ cart.item_count }}</span> items in your cart.
<script type="text/javascript">
$(document).on('cart.requestComplete', function(event, cart) {
$('#counter').html(cart.item_count);
});
</script>
A complete list of the events triggered by Cart.js is available in the Event Reference.
DOM Binding
Full documentation for the DOM Binding module is coming soon.
In the meantime, you can check out the example on the front page in combination with the Rivets.js documentation to get an idea of how things work.