Shopify Terms Checkbox Generator
A cart-page checkbox that greys out Checkout until a shopper agrees to your policies.
Your details
Your snippet file (snippets/terms-checkbox.liquid)
{%- comment -%}
Cart terms checkbox. Render this once inside your cart form, above the Checkout button:
{% render 'terms-checkbox' %}
It greys out and blocks a normal form submit until the box is checked.
It CANNOT gate Shop Pay, PayPal, Google Pay or any other dynamic/express checkout button, since
those submit straight to Shopify without running this page's JavaScript. For a check that also
covers those, turn on Settings → Checkout → the "require customers to agree to your terms"
option under Order processing alongside this snippet.
{%- endcomment -%}
{%- style -%}
.ea-tc{margin:.75rem 0}
.ea-tc__row{display:flex;align-items:flex-start;gap:.5rem;font-size:.9em;cursor:pointer}
.ea-tc__row input{margin-top:.2em;accent-color:#1a1a1a}
.ea-tc__row a{color:#1a1a1a}
.ea-tc__error{color:#b3261e;font-size:.85em;margin:.35rem 0 0}
button[disabled].ea-tc-disabled{opacity:.5;cursor:not-allowed}
{%- endstyle -%}
{%- liquid
if shop.terms_of_service
assign terms_link = '<a href="' | append: shop.terms_of_service.url | append: '" target="_blank" rel="noopener">' | append: shop.terms_of_service.title | downcase | append: '</a>'
else
assign terms_link = 'terms of service'
endif
if shop.privacy_policy
assign privacy_link = '<a href="' | append: shop.privacy_policy.url | append: '" target="_blank" rel="noopener">' | append: shop.privacy_policy.title | downcase | append: '</a>'
else
assign privacy_link = 'privacy policy'
endif
if shop.refund_policy
assign refund_link = '<a href="' | append: shop.refund_policy.url | append: '" target="_blank" rel="noopener">' | append: shop.refund_policy.title | downcase | append: '</a>'
else
assign refund_link = 'refund policy'
endif
assign label = "I have read and agree to the [terms] and [refund]"
assign label = label | replace: '[terms]', terms_link | replace: '[privacy]', privacy_link | replace: '[refund]', refund_link
-%}
<div class="ea-tc" data-terms-checkbox>
<label class="ea-tc__row">
<input type="checkbox" id="ea-terms-checkbox-input" data-terms-input>
<span>{{ label }}</span>
</label>
<p class="ea-tc__error" id="ea-terms-checkbox-error" hidden>Please confirm you agree to our terms before checking out.</p>
</div>
<script>
(function () {
var box = document.getElementById('ea-terms-checkbox-input');
var error = document.getElementById('ea-terms-checkbox-error');
if (!box) return;
var form = box.closest('form');
var button = form ? form.querySelector('[name="checkout"], [type="submit"]') : null;
function sync() {
if (true && button) {
button.disabled = !box.checked;
button.classList.toggle('ea-tc-disabled', !box.checked);
}
if (box.checked && error) error.hidden = true;
}
box.addEventListener('change', sync);
sync();
if (form) {
form.addEventListener('submit', function (e) {
if (!box.checked) {
e.preventDefault();
if (error) error.hidden = false;
box.focus();
}
});
}
})();
</script>To install: in Shopify admin go to Online Store → Themes, click … next to your theme → Edit code. Under Snippets choose Add a new snippet, name it as shown above, paste this code and save. Then add {% render 'terms-checkbox' %} inside your cart form (templates/cart.liquid, or sections/main-cart-footer.liquid on Online Store 2.0 themes), just above the Checkout button.
This only gates the standard Checkout button. Express/dynamic checkout buttons (Shop Pay, PayPal, Apple Pay, Google Pay) bypass it entirely, since they submit without this page's form.
Live preview
Linked to your real policies, not a static link
Write your label with [terms], [privacy] and [refund] wherever you want a link, and the snippet swaps each one for a real link to shop.terms_of_service, shop.privacy_policy or shop.refund_policy, Shopify's own policy objects. If you haven't filled in a policy yet under Settings → Policies, that word shows as plain text instead of a broken link.
By default it disables the Checkout button until the box is checked, and it also blocks a normal form submit either way (in case a theme re-enables the button), showing your error message and moving focus to the checkbox.
What it can't gate
This snippet only intercepts the standard cart form submit. Express and dynamic checkout buttons, Shop Pay, PayPal, Apple Pay, Google Pay, bypass it entirely, since clicking them submits straight to Shopify's own checkout without running this page's JavaScript. That's a limitation of any cart-page checkbox, not just this one.
For a check that also covers those buttons, turn on Shopify's own setting: Settings → Checkout, under Order processing, the option requiring customers to agree to your terms before completing checkout. Use both together for the most complete coverage.
Adding it to your theme
- 01
Add the snippet
Online Store → Themes → Edit code → Snippets → Add a new snippet named
terms-checkbox, paste the code. - 02
Place it in your cart
Add
{% render 'terms-checkbox' %}inside your cart form, just above the Checkout button. That's usuallytemplates/cart.liquidon a vintage theme, orsections/main-cart-footer.liquidon an Online Store 2.0 theme.
Questions people ask
Will this stop someone using Shop Pay without agreeing?
No. Shop Pay and other express checkout buttons submit directly to Shopify's checkout, bypassing this page's form entirely. Combine this with Shopify's native Settings → Checkout agreement option for full coverage.
What if I haven't filled in a Terms of Service page?
The corresponding word in your label shows as plain, unlinked text instead of breaking. Fill in the policy under Settings → Policies to turn it into a real link.
Can I require more than one policy?
Yes, include more than one of [terms], [privacy] and [refund] in your label and all of them get linked.
Does this log that a shopper agreed?
No, it only blocks submission client-side. It doesn't record consent anywhere; if you need a stored record for compliance, look at an app built for that.
Related tools
Last reviewed 30 September 2026.
Sources: Shopify: policy object (terms_of_service, privacy_policy, refund_policy)
