Add To Cart Form (tag pair)
add_to_cart_form builds a POST form that submits to CartThrob’s add_to_cart_action and adds one item (or an on-the-fly item) to the cart.
A video tutorial on this tag is available.
See also:
Parameters
In addition to these tag-specific parameters, this tag also supports global form parameters.
Common global parameters used with this tag include:
returnsecure_returnerror_handlingshow_errors(defaultyes)requiredrules:*actionsecure_actionidclassnameonsubmitenctypelogged_out_redirect
Core Item Parameters
entry_id
Entry ID for the product being added.
entry_id="{entry_id}"
quantity
Quantity to add. If omitted or empty, the action defaults to 1.
quantity="1"
on_the_fly
Set to yes/y/true to add an item without a product entry.
on_the_fly="yes"
title
Title for an on-the-fly item (or to override item title in the add request).
title="Red T-Shirt"
language
Language used for CartThrob messages generated during this form flow.
language="en"
Price, Shipping, and Weight Parameters
price
Set a fixed item price for this submission.
price="9.95"
When price is provided as a tag parameter, it is encoded and takes precedence over user-entered price input.
shipping
Set a fixed item shipping amount for this submission.
shipping="4.95"
When shipping is provided as a tag parameter, it is encoded and takes precedence over user-entered shipping input.
weight
Set a fixed item weight for this submission.
weight="20"
When weight is provided as a tag parameter, it is encoded and takes precedence over user-entered weight input.
allow_user_price
Allow customer input named price to be used for the item price.
allow_user_price="yes"
<input type="text" maxlength="7" size="5" name="price">
This changes the cart item’s price before normal subtotal, discount,
shipping, and tax calculation. It is different from
checkout_form allow_user_price="yes", which replaces the final payable
order total without repricing item rows.
The browser controls this value. Enforce donation minimums, maximums, or other pricing rules on the server when required.
allow_user_shipping
Allow customer input named shipping to be used for item shipping.
allow_user_shipping="yes"
<input type="hidden" name="shipping" value="{shipping_cost_field}" />
allow_user_weight
Allow customer input named weight to be used for item weight.
allow_user_weight="yes"
<input type="hidden" name="weight" value="{weight_field}" />
Tax and Shipping Exemption Parameters
Normally you omit these parameters. For a catalog product, CartThrob looks at the entry’s mapped Tax Exempt and Shipping Exempt fields in CartThrob › Products whenever it calculates the cart. If the entry is taxable, this item is taxed. If the entry is shippable, this item can get shipping. The template does not forward those fields.
If you set no_tax="yes" (alias tax_exempt="yes"), this cart item
is stored as non-taxable. CartThrob skips tax for it even when the
product entry is taxable.
If you set no_shipping="yes" (alias shipping_exempt="yes"), this
cart item is stored as non-shippable. CartThrob skips shipping for it
even when the product entry is shippable.
After this form submits, that setting lives on the cart item. From
then on, cart tags, cart totals, and checkout treat this item the
same as any other cart item that is already non-taxable or
non-shippable. You do not set no_tax or no_shipping again on
cart_info, {cart_total}, or update_cart_form.
Use these parameters when there is no product entry to map (a donation or other on-the-fly item), or when this one add-to-cart should ignore the entry.
Donation — there is no entry, so nothing to map. no_tax and
no_shipping store “skip tax and shipping” on this item:
{exp:cartthrob:add_to_cart_form
on_the_fly="yes"
title="Donation"
allow_user_price="yes"
no_tax="yes"
no_shipping="yes"
return="store/view_cart"}
<label>Amount</label>
<input type="text" name="price">
<button type="submit">Add Donation</button>
{/exp:cartthrob:add_to_cart_form}
Catalog product — normally shipping follows the entry. no_shipping
stores “do not ship this item” instead (for example a downloadable
sample or a pickup-only page):
{exp:cartthrob:add_to_cart_form
entry_id="{entry_id}"
no_shipping="yes"
return="store/view_cart"}
<button type="submit">Add to Cart</button>
{/exp:cartthrob:add_to_cart_form}
no_tax
Stores this added item as non-taxable instead of using the product entry’s Tax Exempt field.
no_tax="yes"
tax_exempt
Alias of no_tax. Both map to the same behavior.
tax_exempt="yes"
no_shipping
Stores this added item as non-shippable instead of using the product entry’s Shipping Exempt field.
no_shipping="yes"
shipping_exempt
Alias of no_shipping. Both map to the same behavior.
shipping_exempt="yes"
To skip tax or shipping for the whole checkout instead of this item,
use checkout_form. See
Which control should I use?.
Item Metadata Parameters
license_number
When enabled, marks the item so license metadata is generated at checkout.
license_number="yes"
expiration_date
Sets expires item metadata (days from now) for purchased-item workflows.
expiration_date="365"
permissions
Sets item permissions metadata.
permissions="members|gold"
class
Overrides the CartThrob item class used when creating the cart item.
class="product"
Upload Parameters
upload_directory
Upload destination used when userfile is submitted. Can be an EE upload directory ID or path.
upload_directory="1"
enctype
Required when using file upload input (userfile).
enctype="multipart/form-data"
Upload input name must be userfile; otherwise CartThrob will not process the file.
Array-Style Parameters
item_options:*
Set item options directly as tag parameters.
item_options:shirt_size="XL"
item_options:shirt_color="Red"
Equivalent form fields:
<input type="text" name="item_options[shirt_size]" value="XL">
<input type="text" name="item_options[shirt_color]" value="Red">
Requiring product option choices
Standard Price Modifiers and Price Modifiers Configurator are different data models and must not use the same storefront field names or required-selection assumptions.
Standard Price Modifiers and ordinary item options
A standard Price Modifiers field is one independent set of choices, such as Color. Standard fields and ordinary item options are optional by default. If a field is omitted or submitted blank, CartThrob records no selected value or price adjustment from that field. With no options selected, the item uses the product’s base price. CartThrob does not silently select the first modifier row.
If a standard Price Modifiers field is the product channel’s mapped
Inventory field, omitting it does not assign or reduce a specific option
row. An explicitly submitted value must match a stored option_value;
unknown and malformed submitted values are rejected.
Use the form’s server-side required parameter when the customer must
make a choice:
{exp:cartthrob:add_to_cart_form
entry_id="{entry_id}"
required="item_options[product_color]"}
<select name="item_options[product_color]" required>
<option value="">Choose a color</option>
<option value="brown">Brown</option>
<option value="black">Black</option>
</select>
<button type="submit">Add to cart</button>
{/exp:cartthrob:add_to_cart_form}
Requiring item_options[product_color] requires one value from that
field’s available choices. To require one value from every listed option
field, separate the field names with |:
required="item_options[product_color]|item_options[product_size]"
The equivalent rules syntax is:
rules:item_options[product_color]="required"
The tag parameter or rules:* setting provides server-side validation.
The HTML required attribute is a useful browser-side addition, but it
is not a substitute for server validation. Include a blank placeholder
option when the customer must actively choose; otherwise a browser
normally submits the first <option> automatically.
Price Modifiers Configurator
A Configurator is one field containing several option groups that resolve to one generated combination/SKU. Its submitted names include the Configurator field and group:
item_options[configuration:shirt_configurator:color]
item_options[configuration:shirt_configurator:size]
When the Configurator is mapped as product Inventory, the form must
submit enough selectable group values to resolve one complete valid
generated combination. CartThrob’s inventory resolver enforces this
even without the form’s required parameter because it must know which
combination row to check and reduce.
That automatic completeness requirement applies only when the
Configurator supplies product Inventory. A Configurator used only for
pricing, weight, or ordinary configuration does not make its generated
group inputs mandatory. Use required when those groups must be
completed.
You may additionally list the generated group inputs in required to
produce earlier, field-specific form validation:
required="item_options[configuration:shirt_configurator:color]|item_options[configuration:shirt_configurator:size]"
Here, | means both generated fields are required—one value from Color
and one from Size. It does not mean “either Color or Size.” Do not use
item_options[shirt_configurator]; that is not a Configurator group
input.
See Price Modifiers Configurator FieldType for complete setup and output examples.
meta:*
Set arbitrary item metadata keys.
meta:source="landing-page"
meta:campaign="spring-sale"
Equivalent form fields:
<input type="hidden" name="meta:source" value="landing-page">
Subscription-Compatible Parameters
The form supports subscription option payloads used by CartThrob’s subscription flows:
subscriptionsubscription_allow_modificationsubscription_namesubscription_descriptionsubscription_start_datesubscription_end_datesubscription_interval_unitssubscription_interval_lengthsubscription_trial_interval_unitssubscription_trial_interval_lengthsubscription_pricesubscription_trial_pricesubscription_total_occurrencessubscription_trial_occurrencessubscription_plan_id
Use direct subscription_* parameters when you need fully custom recurring settings per form. Use subscription_plan_id when the product should follow a predefined subscription plan.
Subscription examples
Example A: Direct subscription fields
{exp:cartthrob:add_to_cart_form
entry_id="{entry_id}"
subscription="yes"
subscription_name="Monthly Coffee Club"
subscription_description="Fresh beans delivered monthly"
subscription_interval_units="months"
subscription_interval_length="1"
subscription_price="19.99"
subscription_trial_price="9.99"
subscription_trial_occurrences="1"
subscription_total_occurrences="12"
subscription_allow_modification="yes"
return="store/view_cart"}
<button type="submit">Start Subscription</button>
{/exp:cartthrob:add_to_cart_form}
This item-level pattern is preferred for donations because the donation
item, cart subtotal, order components, saved total, and gateway charge
can reconcile. Follow it with an ordinary checkout_form; do not apply
a second checkout-level final-total override unless that difference is
intentional.
Example B: Plan-based subscription
{exp:cartthrob:add_to_cart_form
entry_id="{entry_id}"
subscription_plan_id="3"
return="store/view_cart"}
<button type="submit">Add Subscription Plan</button>
{/exp:cartthrob:add_to_cart_form}
Plan-based setup is usually the simpler default when your subscription plan data already exists.
Variables
Inside the tag pair, CartThrob parses:
- Global tag variables (including cart/customer/custom data values)
- Form error variables (when using
error_handling="inline") - Item option helper variables for the current
entry_id
inventory:reduce
Use this variable to add inventory-reduction metadata for another entry during this add-to-cart flow.
{inventory:reduce entry_id="6" quantity="4"}
Notes:
entry_idis required.quantityis optional and defaults to1.- You can include this variable multiple times in one form.
options:* helper pairs
You can pass options:* parameters and render encoded option choices in the tag body:
options:gift_wrap="none:No Wrap|basic:Basic Wrap|premium:Premium Wrap"
{options:gift_wrap selected="basic"}
<label>
<input type="radio" name="item_options[gift_wrap]" value="{option_value}"{checked}>
{option_name}
</label>
{/options:gift_wrap}
Available variables include {option_value}, {option_name}, {selected}, and {checked}.
Form Fields
Common posted fields for this form:
entry_idquantitytitlelanguageprice(whenallow_user_price="yes")shipping(whenallow_user_shipping="yes")weight(whenallow_user_weight="yes")item_options[...]meta:*userfile
Example field names:
<input type="hidden" name="entry_id" value="123">
<input type="text" name="quantity" value="1">
<input type="text" name="item_options[engraving]" value="For Alex">
<input type="hidden" name="meta:source" value="homepage">
<input type="file" name="userfile">
Behavior and Constraints
Form processing flow:
- Form rules are validated by CartThrob form builder.
- Customer info is saved.
- Item payload is assembled from encoded tag params and submitted fields.
- Item is added and optional metadata/options are attached.
- Success/error handling follows form builder behavior (
return,secure_return,error_handling,show_errors).
Important implementation details:
- CartThrob checks the requested quantity against current inventory when the form is submitted. This server-side check still applies if a template mistakenly displays an Add to Cart button for an unavailable product.
- The requested quantity and the matching quantity already in the cart are both considered when enforcing a finite stock level.
- For standard Price Modifiers inventory, a submitted
item_options[FIELD_SHORT_NAME]selection determines which option row is checked. An omitted or blank optional field leaves that choice open-ended and applies no price adjustment from that field; userequiredwhen a selection must be recorded. - For Configurator inventory, the generated
item_options[configuration:FIELD_SHORT_NAME:GROUP_SHORT_NAME]values must resolve to one complete valid combination/SKU. tax_exemptandshipping_exemptare aliases forno_taxandno_shipping.- Fixed
price/shipping/weightparams override user-entered values. show_errorsdefaults toyesunless set otherwise.upload_directoryis only used when auserfileupload is submitted.- Uploaded file info is added as item options:
upload(file name)upload_directory(file path)
meta:*keys are merged into item metadata without replacing keys already set earlier in the action.logged_out_redirectis honored for logged-out users.
Examples
Stock-aware Product Form
{exp:channel:entries channel="products" limit="1"}
{exp:cartthrob:add_to_cart_form
entry_id="{entry_id}"
return="store/view_cart"
error_handling="inline"}
{if '{exp:cartthrob:is_in_stock entry_id="{entry_id}"}'}
<p class="stock-status in-stock">In stock</p>
<label for="quantity-{entry_id}">Quantity</label>
<input
id="quantity-{entry_id}"
type="number"
name="quantity"
value="1"
min="1"
>
<button type="submit">Add to Cart</button>
{if:else}
<p class="stock-status out-of-stock">Out of stock</p>
{/if}
{if errors_exist}
<ul class="errors">
{errors}
<li>{error}</li>
{/errors}
</ul>
{/if}
{/exp:cartthrob:add_to_cart_form}
{/exp:channel:entries}
is_in_stock controls the storefront message and button. The
add_to_cart_form action remains the authority and rejects unavailable
items or quantities greater than the available stock. Always render the
form’s errors so a customer gets a useful response if inventory changes
between page load and submission.
For products whose inventory is tracked per modifier, is_in_stock
means at least one option is available. Use the {inventory} value
inside the item_options option
loop to label or disable individual unavailable choices. CartThrob
validates the selected option again when this form is submitted.
Donation / On-the-Fly Form
{exp:cartthrob:add_to_cart_form
on_the_fly="yes"
title="Donation"
allow_user_price="yes"
no_tax="yes"
no_shipping="yes"
return="store/view_cart"}
<label>Donation Amount</label>
<input type="text" name="price">
<label>Note</label>
<input type="text" name="item_options[donation_note]">
<button type="submit">Add Donation</button>
{/exp:cartthrob:add_to_cart_form}
no_tax and no_shipping mark this cart item. There is no product
entry to map. See Tax and Shipping Exemption Parameters.
Upload with Add to Cart
{exp:cartthrob:add_to_cart_form
entry_id="{entry_id}"
upload_directory="1"
enctype="multipart/form-data"
return="store/view_cart"}
<input type="file" name="userfile">
<button type="submit">Add Item</button>
{/exp:cartthrob:add_to_cart_form}
Inventory Side-Effect
{exp:cartthrob:add_to_cart_form entry_id="{entry_id}" return="store/view_cart"}
{inventory:reduce entry_id="6" quantity="1"}
<button type="submit">Add to Cart</button>
{/exp:cartthrob:add_to_cart_form}