CartThrob 9 documentation for ExpressionEngine commerce teams Visit CartThrob.com
CartThrob.com

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:

  • return
  • secure_return
  • error_handling
  • show_errors (default yes)
  • required
  • rules:*
  • action
  • secure_action
  • id
  • class
  • name
  • onsubmit
  • enctype
  • logged_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:

  • subscription
  • subscription_allow_modification
  • subscription_name
  • subscription_description
  • subscription_start_date
  • subscription_end_date
  • subscription_interval_units
  • subscription_interval_length
  • subscription_trial_interval_units
  • subscription_trial_interval_length
  • subscription_price
  • subscription_trial_price
  • subscription_total_occurrences
  • subscription_trial_occurrences
  • subscription_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_id is required.
  • quantity is optional and defaults to 1.
  • 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_id
  • quantity
  • title
  • language
  • price (when allow_user_price="yes")
  • shipping (when allow_user_shipping="yes")
  • weight (when allow_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:

  1. Form rules are validated by CartThrob form builder.
  2. Customer info is saved.
  3. Item payload is assembled from encoded tag params and submitted fields.
  4. Item is added and optional metadata/options are attached.
  5. 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; use required when 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_exempt and shipping_exempt are aliases for no_tax and no_shipping.
  • Fixed price/shipping/weight params override user-entered values.
  • show_errors defaults to yes unless set otherwise.
  • upload_directory is only used when a userfile upload 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_redirect is 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}