Add-to-cart links and buttons that keep the cart
Add a product to the Shopify cart without emptying it: the product form to /cart/add with return_to, the /cart/add.js call, and when to use a permalink instead.
- Form
- POST /cart/add ({% form 'product' %})
- JavaScript
- POST /cart/add.js with items[]
- Plain link
- Cart permalink /cart/<variant ID>:<qty>
- Plan
- Every plan
- Last checked
In short
Shopify documents two ways to add to the existing cart: a product form that posts to /cart/add, and a POST to /cart/add.js from JavaScript. By default the form then opens the cart page; return_to: 'back' keeps the customer where they were. For a plain link in an email or bio, use a cart permalink (/cart/<variant ID>:<quantity>), which replaces the cart and opens checkout.
How to use it
- Inside a product template or section, wrap a hidden id input (the variant ID) and a quantity in {% form 'product', product %}.
- Add return_to: 'back' to the form tag to send the customer back to the same page instead of the cart.
- For an add without a page load, POST JSON {items: [{id, quantity}]} to Shopify.routes.root + 'cart/add.js' and handle a 422 response.
- For a link that works anywhere, build a cart permalink instead and test it in a private window.
Code
add-to-cart.js// Adds a variant to the cart without leaving the page. Shopify.routes.root
// keeps the market's language path (/fr/), so prices stay in the right currency.
async function addToCart(variantId, quantity = 1) {
const response = await fetch(window.Shopify.routes.root + 'cart/add.js', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({items: [{id: variantId, quantity}]}),
});
const data = await response.json();
// 422 means sold out, not enough stock, or an unknown variant ID.
if (!response.ok) throw new Error(data.description || 'Could not add to cart');
return data;
}
addToCart(12345678901234, 2).catch((error) => console.error(error.message));
add-to-cart-form.liquid{%- comment -%}
An add-to-cart button that keeps the customer on the page they were on.
The product form posts to /cart/add, which adds to the existing cart
(a /cart/ permalink replaces it). return_to: 'back' overrides the default
redirect to the cart page.
{%- endcomment -%}
{%- assign variant = product.selected_or_first_available_variant -%}
{% form 'product', product, return_to: 'back' %}
<input type="hidden" name="id" value="{{ variant.id }}">
<input type="hidden" name="quantity" value="1">
<button type="submit" {% unless variant.available %}disabled{% endunless %}>
Add to cart
</button>
{% endform %}
which-link.txt# Documented ways to add to the cart
POST /cart/add product form ({% form 'product' %}), adds to the existing cart
POST /cart/add.js Ajax Cart API, returns JSON, adds to the existing cart
GET /cart/<variant ID>:<qty> cart permalink, replaces the cart and opens checkout
# A plain link someone can click (email, chat, social bio): use a cart permalink
https://your-store.com/cart/12345678901234:1
# Same, but open the cart page instead of checkout
https://your-store.com/cart/12345678901234:1?storefront=true
# return_to values for the product form
return_to: 'back' the page the customer was on
return_to: '/collections/all' a relative path
return_to: routes.root_url a routes attribute
Written by Lintel. MIT license: free to use in your own store and in client work.
Limits and edge cases
- A GET link such as /cart/add?id=…&quantity=… isn’t part of Shopify’s documentation, so don’t rely on it in campaigns; cart permalinks are the documented link format.
- If you ask for more than is in stock, /cart/add.js adds what’s available and returns a 422 error with a description.
- Changes made through the GraphQL Admin API can take a few seconds to reach the Cart API; a brand-new variant may return “Cannot find variant” at first.
- Build the URL from Shopify.routes.root or routes.cart_add_url so a visitor on /fr/ keeps their language and currency.
Questions
- How do I add to cart in Shopify without going to the cart page?
- Add return_to: 'back' to the product form tag, or send the request to /cart/add.js with JavaScript and update the cart drawer yourself.
- Does /cart/add?id= work as a link?
- Shopify documents /cart/add as a form POST and /cart/add.js as a JSON POST, not as a GET link. For a clickable link, use a cart permalink like /cart/12345678901234:1.
- What is the difference between /cart/add and a cart permalink?
- /cart/add adds to the cart the customer already has. A cart permalink replaces the cart with the items in the link and opens checkout (or the cart page with ?storefront=true).
- Can I add several products at once?
- Yes. /cart/add.js takes an items array with one object per variant, and a cart permalink takes comma-separated variant:quantity pairs.
Shopify documentation
- Checked on a Shopify store on the date shown. Shopify changes its admin often; if a step looks different, the idea usually still works.
- Examples use placeholder IDs and your-store.myshopify.com. Replace them with your own.
- MIT license for the code snippets.
Need this built, tested and maintained for your store? See Shopify development.
