Commerce Filters
Nimbu provides specialized filters for e-commerce functionality, making it easy to create shopping carts, checkout flows, and payment integrations.
Add to Cart
add_to_cart
Generate complete add-to-cart form:
liquid
{{ product | add_to_cart }}
<!-- Generates: Complete form with product, quantity, and submit button -->
{{ product | add_to_cart, btn_text: 'Add to Bag', btn_class: 'btn btn-primary' }}
{{ product | add_to_cart, default_quantity: 2, show_quantity: true }}Parameters:
btn_text- Button label textbtn_class- CSS class for buttondefault_quantity- Initial quantity (default: 1)show_quantity- Show quantity selector (default: false)disabled- Disable the button
Generated HTML:
html
<form class="add-to-cart" method="post" action="/cart/add">
<!-- CSRF token and product data -->
<input type="hidden" name="product_id" value="123">
<input type="hidden" name="quantity" value="1">
<button type="submit" class="btn btn-primary">Add to Cart</button>
</form>Custom Add to Cart
Build custom forms with more control:
liquid
<form action="/cart/add" method="post" class="product-form">
{% csrf_token %}
<input type="hidden" name="product_id" value="{{ product.id }}">
<!-- Variant selector -->
{% if product.variants.size > 1 %}
<select name="variant_id" required>
{% for variant in product.variants %}
<option value="{{ variant.id }}" {% unless variant.available %}disabled{% endunless %}>
{{ variant.title }} - {{ variant.price | money_with_currency }}
</option>
{% endfor %}
</select>
{% else %}
<input type="hidden" name="variant_id" value="{{ product.variants.first.id }}">
{% endif %}
<!-- Quantity -->
<input type="number"
name="quantity"
value="1"
min="1"
max="{{ product.inventory_quantity }}">
<button type="submit" class="add-to-cart-btn">
Add to Cart
</button>
</form>Cart Management
update_cart_item
Generate form to update cart item quantity:
liquid
{{ line_item | update_cart_item }}
{{ line_item | update_cart_item, btn_text: 'Update', btn_class: 'btn-sm' }}Generated HTML:
html
<form class="update-cart-item" method="post" action="/cart/items/456/update">
<input type="number" name="quantity" value="2" min="0">
<button type="submit">Update</button>
</form>delete_cart_item
Generate delete link for cart item:
liquid
{{ line_item | delete_cart_item }}
{{ line_item | delete_cart_item, delete: 'Remove', class: 'text-danger' }}
{{ line_item | delete_cart_item, confirm: 'Remove this item?' }}Parameters:
delete- Link text (default: "Delete")class- CSS classconfirm- Confirmation message
Checkout Integration
checkout_button
Generate checkout button with cart data:
liquid
{{ cart | checkout_button }}
{{ cart | checkout_button, text: 'Proceed to Checkout', class: 'btn-lg btn-success' }}payment_form
Create payment method form:
liquid
{{ order | payment_form, provider: 'stripe' }}
{{ order | payment_form, provider: 'mollie', class: 'payment-section' }}Supported Providers:
stripe- Stripe Checkoutmollie- Mollie Paymentspaypal- PayPal Buttons
Coupon Helpers
apply_coupon_form
Generate coupon application form:
liquid
{{ cart | apply_coupon_form }}
{{ cart | apply_coupon_form, placeholder: 'Enter discount code', btn_text: 'Apply' }}Generated Form:
html
<form class="apply-coupon" method="post" action="/cart/coupons">
<input type="text" name="coupon_code" placeholder="Discount code">
<button type="submit">Apply Coupon</button>
</form>remove_coupon
Remove applied coupon:
liquid
{{ coupon | remove_coupon, text: 'Remove', class: 'text-sm' }}Practical Examples
Product Page with Variants
liquid
<div class="product-page">
<div class="product-images">
<!-- Product gallery -->
</div>
<div class="product-details">
<h1>{{ product.name }}</h1>
<div class="price">
{% if product.on_sale %}
<span class="original-price">
{{ product.compare_at_price | money_with_currency }}
</span>
<span class="sale-price">
{{ product.price | money_with_currency }}
</span>
{% else %}
{{ product.price | money_with_currency }}
{% endif %}
</div>
<div class="product-form">
<form action="/cart/add" method="post">
{% csrf_token %}
<input type="hidden" name="product_id" value="{{ product.id }}">
{% if product.variants.size > 1 %}
<div class="variant-selector">
<label for="variant-select">Select Option:</label>
<select name="variant_id" id="variant-select" required>
{% for variant in product.variants %}
<option
value="{{ variant.id }}"
data-price="{{ variant.price }}"
data-available="{{ variant.available }}"
{% unless variant.available %}disabled{% endunless %}>
{{ variant.title }}
{% unless variant.available %} - Sold Out{% endunless %}
</option>
{% endfor %}
</select>
</div>
{% else %}
<input type="hidden" name="variant_id" value="{{ product.variants.first.id }}">
{% endif %}
<div class="quantity-selector">
<label for="quantity">Quantity:</label>
<input
type="number"
name="quantity"
id="quantity"
value="1"
min="1"
max="{{ product.inventory_quantity }}">
</div>
<button
type="submit"
class="btn btn-primary btn-lg"
{% unless product.available %}disabled{% endunless %}>
{% if product.available %}
Add to Cart
{% else %}
Sold Out
{% endif %}
</button>
</form>
</div>
</div>
</div>Shopping Cart Page
liquid
<div class="cart-page">
<h1>Shopping Cart</h1>
{% if cart.items.size > 0 %}
<div class="cart-items">
{% for item in cart.items %}
<div class="cart-item">
<div class="item-image">
{% if item.product.images.first %}
<img
src="{{ item.product.images.first.url | filter, width: '150px', height: '150px', cropping: 'fill' }}"
alt="{{ item.product.name }}">
{% endif %}
</div>
<div class="item-details">
<h3>
<a href="{{ item.product.url }}">{{ item.product.name }}</a>
</h3>
{% if item.variant.title != 'Default Title' %}
<p class="variant">{{ item.variant.title }}</p>
{% endif %}
<p class="price">
{{ item.price | money_with_currency }}
</p>
</div>
<div class="item-quantity">
{{ item | update_cart_item, btn_text: 'Update', btn_class: 'btn-sm' }}
</div>
<div class="item-total">
{{ item.line_price | money_with_currency }}
</div>
<div class="item-remove">
{{ item | delete_cart_item, delete: '×', class: 'remove-btn' }}
</div>
</div>
{% endfor %}
</div>
<div class="cart-summary">
<div class="subtotal">
<span>Subtotal:</span>
<span>{{ cart.subtotal | money_with_currency }}</span>
</div>
{% if cart.total_discount > 0 %}
<div class="discount">
<span>
Discount
{% if cart.coupon %}
({{ cart.coupon.code }})
{{ cart.coupon | remove_coupon, text: 'Remove' }}
{% endif %}:
</span>
<span class="negative">
-{{ cart.total_discount | money_with_currency }}
</span>
</div>
{% endif %}
<div class="shipping">
<span>Shipping:</span>
<span>
{% if cart.shipping_cost > 0 %}
{{ cart.shipping_cost | money_with_currency }}
{% else %}
Calculated at checkout
{% endif %}
</span>
</div>
<div class="total">
<span>Total:</span>
<span>{{ cart.total | money_with_currency }}</span>
</div>
<!-- Coupon form -->
{% unless cart.coupon %}
<div class="coupon-section">
{{ cart | apply_coupon_form, placeholder: 'Discount code', btn_text: 'Apply' }}
</div>
{% endunless %}
<!-- Checkout button -->
<div class="checkout-actions">
{{ cart | checkout_button, text: 'Proceed to Checkout', class: 'btn btn-lg btn-success' }}
</div>
</div>
{% else %}
<div class="empty-cart">
<p>Your cart is empty.</p>
<a href="/shop" class="btn btn-primary">Continue Shopping</a>
</div>
{% endif %}
</div>Quick Add to Cart (AJAX)
liquid
<div class="product-card">
<a href="{{ product.url }}">
<img
src="{{ product.images.first.url | filter, width: '400px', height: '400px', cropping: 'fill' }}"
alt="{{ product.name }}">
</a>
<h3>
<a href="{{ product.url }}">{{ product.name }}</a>
</h3>
<p class="price">{{ product.price | money_with_currency }}</p>
<form
action="/cart/add"
method="post"
class="quick-add-form"
data-ajax-cart>
{% csrf_token %}
<input type="hidden" name="product_id" value="{{ product.id }}">
<input type="hidden" name="variant_id" value="{{ product.variants.first.id }}">
<input type="hidden" name="quantity" value="1">
<button
type="submit"
class="btn btn-sm"
{% unless product.available %}disabled{% endunless %}>
{% if product.available %}
Quick Add
{% else %}
Sold Out
{% endif %}
</button>
</form>
</div>
<script>
document.querySelectorAll('[data-ajax-cart]').forEach(form => {
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
const response = await fetch(form.action, {
method: 'POST',
body: formData,
headers: {
'Accept': 'application/json'
}
});
if (response.ok) {
const cart = await response.json();
// Update cart UI
updateCartCount(cart.item_count);
showNotification('Added to cart!');
}
});
});
</script>Checkout Page with Payment
liquid
<div class="checkout-page">
<div class="checkout-content">
<div class="checkout-steps">
<!-- Step 1: Customer Info -->
<div class="step" data-step="customer">
<h2>Customer Information</h2>
<!-- Customer form -->
</div>
<!-- Step 2: Shipping -->
<div class="step" data-step="shipping">
<h2>Shipping Address</h2>
<!-- Shipping form -->
</div>
<!-- Step 3: Payment -->
<div class="step" data-step="payment">
<h2>Payment Method</h2>
{{ order | payment_form, provider: 'stripe', class: 'payment-section' }}
<!-- Or custom payment selector -->
<div class="payment-methods">
<label>
<input type="radio" name="payment_method" value="stripe" checked>
<span>Credit Card</span>
</label>
<label>
<input type="radio" name="payment_method" value="paypal">
<span>PayPal</span>
</label>
<label>
<input type="radio" name="payment_method" value="mollie">
<span>iDEAL / Bancontact</span>
</label>
</div>
</div>
</div>
<!-- Order Summary Sidebar -->
<div class="order-summary">
<h3>Order Summary</h3>
{% for item in cart.items %}
<div class="summary-item">
<span>{{ item.product.name }} × {{ item.quantity }}</span>
<span>{{ item.line_price | money_with_currency }}</span>
</div>
{% endfor %}
<div class="summary-subtotal">
<span>Subtotal:</span>
<span>{{ cart.subtotal | money_with_currency }}</span>
</div>
<div class="summary-shipping">
<span>Shipping:</span>
<span>{{ cart.shipping_cost | money_with_currency }}</span>
</div>
<div class="summary-total">
<span>Total:</span>
<span>{{ cart.total | money_with_currency }}</span>
</div>
</div>
</div>
</div>Best Practices
1. CSRF Protection
Always include CSRF token in forms:
liquid
<!-- ✅ Good: CSRF protected -->
<form action="/cart/add" method="post">
{% csrf_token %}
<!-- form fields -->
</form>
<!-- ❌ Bad: No CSRF protection -->
<form action="/cart/add" method="post">
<!-- vulnerable to CSRF attacks -->
</form>2. Validate Inventory
Check product availability:
liquid
<!-- ✅ Good: Check availability -->
<button
type="submit"
{% unless product.available %}disabled{% endunless %}>
{% if product.available %}Add to Cart{% else %}Sold Out{% endif %}
</button>
<!-- ❌ Bad: No availability check -->
<button type="submit">Add to Cart</button>3. User Feedback
Provide clear feedback for cart operations:
liquid
<form action="/cart/add" method="post" class="add-to-cart-form">
<!-- form fields -->
<div class="form-messages">
{% if cart_error %}
<div class="alert alert-danger">{{ cart_error }}</div>
{% endif %}
{% if cart_success %}
<div class="alert alert-success">Added to cart!</div>
{% endif %}
</div>
<button type="submit">Add to Cart</button>
</form>4. Accessibility
Make forms accessible:
liquid
<form action="/cart/add" method="post">
<label for="quantity">Quantity:</label>
<input
type="number"
id="quantity"
name="quantity"
aria-label="Product quantity"
min="1"
value="1">
<button type="submit" aria-label="Add {{ product.name }} to cart">
Add to Cart
</button>
</form>Next Steps
- Numbers & Money - Format prices and currency
- Webshop - Complete e-commerce guide
- Forms - Form building and validation
- Global Variables - Cart and product drops
Build powerful e-commerce experiences with Nimbu's commerce filters!