# Liquid forms reference

Source: https://support.storeconnect.com/articles/liquid-forms-reference · Last modified 21 August 2026

The `{% form %}` tag generates HTML forms with the correct action URL, CSRF protection, and field definitions. This article lists all available form types and their fields.

For usage patterns, the form drop, and how to extend forms with custom parameters, see [Theme forms](theme-forms).

## How the form tag works


```liquid

{% form "form-type" [, option: value] %}
  {{ form.field_name.label }}
  <input name="{{ form.field_name.name }}" value="{{ form.field_name.value }}">
  <button type="submit">Submit</button>
{% endform %}
```


The tag automatically:
- Wraps the block in a `<form>` element with the correct `action` URL and `method`
- Includes a hidden `authenticity_token` field for CSRF protection
- Makes a `form` drop available inside the block with field definitions and errors

Any option not consumed by the form type itself becomes an HTML attribute on the `<form>` element (`id`, `class`, `data-*`, etc.).

---

## Authentication forms

### `login`

Logs in a customer.


```liquid

{% form "login" %}
  <input type="email" name="{{ form.username.name }}" value="{{ form.username.value }}">
  <input type="password" name="{{ form.password.name }}">
  <button type="submit">Log in</button>
{% endform %}
```


**Fields:** `username`, `password`

### `register`

Registers a new customer.


```liquid

{% form "register" %}
  <input name="{{ form.firstname.name }}" value="{{ form.firstname.value }}" placeholder="First name">
  <input name="{{ form.lastname.name }}" value="{{ form.lastname.value }}" placeholder="Last name">
  <input type="email" name="{{ form.email.name }}" value="{{ form.email.value }}" placeholder="Email">
  <input type="password" name="{{ form.password.name }}" placeholder="Password">
  <button type="submit">Create account</button>
{% endform %}
```


**Fields:** `firstname`, `lastname`, `email`, `password`, `phone` (optional), `campaign_ids` (optional), `company_name` (optional), billing address fields: `billing_address_lines`, `billing_city`, `billing_state`, `billing_postal_code`, `billing_country`

### `sso-login`

Single Sign-On login via an external provider. Renders the SSO redirect mechanism.

### `forgot-password`

Requests a password reset email.

**Fields:** `email`

### `reset-password`

Sets a new password from a reset token.

**Fields:** `password`, `password_confirmation`

### `resend-confirmation`

Resends the email confirmation.

**Fields:** `username`

### `accept-invitation`

Accepts an invitation to create an account.

**Fields:** `password`, `password_confirmation`, `campaign_ids`

### `account-missing-details`

Collects required profile information after login when mandatory fields are missing. Only renders fields with missing values — the set of fields is dynamic.

**Fields:** Varies based on what's missing from the contact profile

---

## Cart forms

### `add-to-cart`

Adds a product to the cart. Pass the product as an option (preferred) or use `product_id`.


```liquid

{% form "add-to-cart", product: current_product %}
  <input type="number" name="{{ form.quantity.name }}" value="1" min="1">
  <button type="submit">Add to cart</button>
{% endform %}
```


**Options:** `product` (ProductDrop, preferred) or `product_id` (String)

**Fields:** `quantity`, `price` (for variable pricing), `product_bookable_location_id`, `booking_start`, `booking_end`, plus any custom form question answers

**"Buy now" pattern** — use `formaction` to redirect directly to checkout:


```liquid

{% form "add-to-cart", product: current_product %}
  <input type="number" name="{{ form.quantity.name }}" value="1">
  <button type="submit">Add to cart</button>
  <input type="submit" formaction="{{ form.path | params: after: 'cart' }}" value="Buy now">
{% endform %}
```


### `add-bundle-to-cart`

Adds a bundle product with its configured components.

**Fields:** `product_id`, bundle configuration fields

### `add-preset-bundle`

Adds a pre-configured bundle to the cart.

### `cart`

Wraps the cart display for quantity updates. No pre-defined form fields — use raw `<input>` elements with the `cart_items[ITEM_ID][quantity]` naming convention.


```liquid

{% form "cart" %}
  {% for item in current_cart.items %}
    <input type="number" name="cart_items[{{ item.id }}][quantity]" value="{{ item.quantity }}">
  {% endfor %}
  <button type="submit">Update cart</button>
{% endform %}
```


To remove a cart item, use the item's `delete_path`:


```liquid

<a href="{{ item.delete_path }}">Remove</a>
```


---

## Checkout forms

### `checkout-customer-information`

Collects customer details at the first checkout step.


```liquid

{% form "checkout-customer-information", id: "SC-CheckoutShippingForm" %}
  {% render "form_errors", errors: form.errors %}
  {% render "checkout/customer_information/form", form: form %}
{% endform %}
```


**Fields:** `email`, `phone`, `company_name`, `first_name`/`last_name` or `full_name` (depends on store config), `shipping_address_lines`, `shipping_city`, `shipping_state`, `shipping_postal_code`, `shipping_country`, `billing_same_as_shipping`, billing address fields, `customer_notes`, `captcha_token`, plus custom form question answers

### `checkout-shipping-information`

Selects the shipping method.


```liquid

{% form "checkout-shipping-information" %}
  {% render "form_errors", errors: form.errors %}
  {% render "checkout/shipping_information/form", form: form %}
{% endform %}
```


**Fields:** `method` (shipping rate ID), `collection_time`, `notes` (if enabled), `use_points`, `click_and_collect_option`, per-item delivery window fields, plus custom form question answers

### `checkout-accept-terms`

Accepts terms and conditions.


```liquid

{% form "checkout-accept-terms", id: "SC-CheckoutTerms" %}
  {% render "form_errors", errors: form.errors %}
  {% render "checkout/accept_terms/form", form: form %}
{% endform %}
```


**Fields:** `terms_accepted`, plus custom form question answers

### `payment`

Submits payment information. The form content varies by payment provider (Stripe, PayPal, Adyen, etc.) and is typically rendered inside a component that loads the payment provider's JavaScript.

### `payment-not-required`

Handles orders that don't require payment — for example, orders fully covered by account credits or using pay-by-account.

---

## Voucher and promotion forms

### `apply-voucher`

Applies a voucher code to the cart.


```liquid

{% form "apply-voucher" %}
  <input name="{{ form.code.name }}" placeholder="Voucher code">
  <input name="{{ form.pin.name }}" placeholder="PIN (if required)">
  <button type="submit">Apply</button>
{% endform %}
```


**Fields:** `code`, `pin`

### `remove-voucher`

Removes an applied voucher. Pass the voucher as an option.


```liquid

{% form "remove-voucher", voucher: voucher %}
  <button type="submit">Remove</button>
{% endform %}
```


### `activate-voucher`

Activates a voucher from the voucher detail page. Pass the voucher as an option.

### `apply-promo-code`

Applies a promotion/coupon code to the cart.


```liquid

{% form "apply-promo-code" %}
  <input name="{{ form.code.name }}" placeholder="Promo code">
  <button type="submit">Apply</button>
{% endform %}
```


**Fields:** `code`

### `remove-promo-code`

Removes an applied promotion code.

---

## Account credit forms

### `apply-account-credit`

Applies account credit to the order.

**Fields:** `id` (credit account ID), `amount` (amount to apply)

### `remove-account-credit`

Removes applied account credit. Requires `account_credit` option.

---

## Account management forms

### `account`

Updates the customer's profile and login credentials.


```liquid

{% form "account" %}
  <input name="{{ form.firstname.name }}" value="{{ form.firstname.value }}">
  <input name="{{ form.lastname.name }}" value="{{ form.lastname.value }}">
  <input type="email" name="{{ form.email.name }}" value="{{ form.email.value }}">
  <input name="{{ form.phone.name }}" value="{{ form.phone.value }}">
  <button type="submit">Update</button>
{% endform %}
```


**Fields:** `username`, `current_password`, `password`, `password_confirmation`, `email`, `firstname`, `lastname`, `phone`, `campaign_ids`, `company_name`, billing address fields (`billing_address_lines`, `billing_city`, `billing_state`, `billing_postal_code`, `billing_country`), shipping address fields

---

## Custom forms

Contact forms, newsletter sign-ups, and other configurable forms are implemented as custom forms in StoreConnect. Use the `custom-form` type with the form's identifier.

### `custom-form`

Submits a custom form defined in the CMS.


```liquid

{% form "custom-form", custom_form: my_form %}
  {% for question in my_form.questions %}
    <div class="field">
      <label>{{ question.label }}</label>
      {% case question.question_type %}
      {% when "text" %}
        <input type="text" name="{{ question.input_name }}" value="{{ question.answer_value }}">
      {% when "text_area" %}
        <textarea name="{{ question.input_name }}">{{ question.answer_value }}</textarea>
      {% when "picklist" %}
        <select name="{{ question.input_name }}">
          {% for option in question.picklist_values %}
            <option value="{{ option }}" {% if question.answer_value == option %}selected{% endif %}>{{ option }}</option>
          {% endfor %}
        </select>
      {% when "integer" %}
        <input type="number" name="{{ question.input_name }}" value="{{ question.answer_value }}">
      {% when "date" %}
        <input type="date" name="{{ question.input_name }}" value="{{ question.answer_value }}">
      {% when "file" %}
        <input type="file" name="{{ question.input_name }}">
      {% endcase %}
    </div>
  {% endfor %}
  <button type="submit">Submit</button>
{% endform %}
```


**Supported question types:** `text`, `text_area`, `picklist`, `multi_picklist`, `integer`, `decimal`, `date`, `datetime`, `file`, `hidden`

---

## Subscription and additional payment forms

### `checkout-set-password`

Creates an account after guest checkout. Requires the `order` option.

**Fields:** `password`, `campaign_ids`

### `additional-payment-billing-address`

Updates the billing address for additional payment (pay-by-link). Requires the order context.

**Fields:** `billing_address_lines`, `billing_city`, `billing_state`, `billing_postal_code`, `billing_country`

### `subscription-payment`

Pays an outstanding subscription installment. Requires the `subscription` option.

### `update-subscription-payment-details`

Updates the payment method on a subscription. Requires the `subscription` option.

---

## Booking forms

### `booking-attendee-add`

Adds an attendee to a booking.

**Fields:** Attendee information fields (firstname, lastname, email, phone, etc.)

### `booking-attendee-edit`

Edits an existing booking attendee.

---

## Privacy forms

### `privacy-accept-all`

Accepts all cookie groups.

### `privacy-reject-all`

Rejects all optional cookie groups.

### `privacy-settings`

Saves specific cookie preferences.

---

## Geolocation forms

### `geolocation-select`

Selects a store based on the customer's location.

### `geolocation-dismiss`

Dismisses the store location suggestion.

---

## Form error handling

All forms expose validation errors when submission fails. Access errors at the form level or per field.


```liquid

{% form "register" %}
  {% if form.errors.size > 0 %}
    <div class="errors">
      <ul>
        {% for error in form.errors %}
          <li>{{ error }}</li>
        {% endfor %}
      </ul>
    </div>
  {% endif %}

  <div class="field {% if form.email.errors.size > 0 %}has-error{% endif %}">
    <label>{{ form.email.label }}</label>
    <input type="email" name="{{ form.email.name }}" value="{{ form.email.value }}">
    {% for error in form.email.errors %}
      <span class="error">{{ error }}</span>
    {% endfor %}
  </div>

  <button type="submit">Register</button>
{% endform %}
```


When a form submission fails:
1. The page re-renders with the same form.
2. `form.errors` contains all validation errors.
3. Field values are preserved via `form.field.value`.
4. Per-field errors are available via `form.field.errors`.

---

## Follow StoreConnect

- [Email Newsletter](https://getstoreconnect.com/c/lp-newsletter)
- [LinkedIn Newsletter](https://www.linkedin.com/build-relation/newsletter-follow?entityUrn=7444956928444862464)
- [YouTube](https://www.youtube.com/channel/UCngKdP2x8l1wcbAKW3tvU8g)
- [LinkedIn](https://www.linkedin.com/company/storeconnect)
- [X / Twitter](https://x.com/storeconnecthq)

## Popular Links

- [Partners](https://getstoreconnect.com/partners)
- [News](https://getstoreconnect.com/articles/news)
- [Events](https://getstoreconnect.com/articles/events)
- [Feature Comparison](https://getstoreconnect.com/how-we-compare)
- [Download a free trial](https://appexchange.salesforce.com/appxListingDetail?listingId=a0N3A00000FMkeKUAT)
- [Book a Demo](https://getstoreconnect.com/contact)

## Documentation

- [Help documentation](https://support.storeconnect.com/help-documentation)
- [AI agents](https://support.storeconnect.com/ai)
- [Videos & tutorials](https://support.storeconnect.com/videos-tutorials)
- [Developer reference](https://support.storeconnect.com/developer-reference)
- [Release notes](https://support.storeconnect.com/release-notes)
- [Troubleshooting](https://support.storeconnect.com/troubleshooting)
- [Trust Center](https://trust.getstoreconnect.com/)
- [Status Page](https://status.storeconnect.com/)

## Contact

- info@getstoreconnect.com
- US +1 415 745 3230
- AUS +61 2 8365 2308

100 S Ashley Dr, Suite 600-2461
Tampa FL 33602-600 USA

Level 22, Sydney Place
180 George Street
Sydney, NSW, 2000, AUS

---

StoreConnect Support — https://support.storeconnect.com/articles/liquid-forms-reference