{"title":"Liquid forms reference","slug":"liquid-forms-reference","url":"https://support.storeconnect.com/articles/liquid-forms-reference","url_markdown":"https://support.storeconnect.com/articles/liquid-forms-reference.md","subtitle":null,"summary":"Reference for the form types available with the Liquid form tag in StoreConnect, organized by category: authentication, cart, checkout, vouchers and promotions, account credits, account management, custom forms, subscriptions and additional payments, bookings, privacy, geolocation, and form error handling.","type":"Developer_Documentation","video_url":"","keywords":"liquid forms, form types, add-to-cart form, login form, register form, checkout forms, custom form, form reference, form tag, form errors, CSRF, authentication token, cart forms, payment forms, booking forms","last_modified":"2026-08-21T07:12:35+0000","body_markdown":"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.\n\nFor usage patterns, the form drop, and how to extend forms with custom parameters, see [Theme forms](theme-forms).\n\n## How the form tag works\n\n\n```liquid\n\n{% form \"form-type\" [, option: value] %}\n  {{ form.field_name.label }}\n  \u003cinput name=\"{{ form.field_name.name }}\" value=\"{{ form.field_name.value }}\"\u003e\n  \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n{% endform %}\n```\n\n\nThe tag automatically:\n- Wraps the block in a `\u003cform\u003e` element with the correct `action` URL and `method`\n- Includes a hidden `authenticity_token` field for CSRF protection\n- Makes a `form` drop available inside the block with field definitions and errors\n\nAny option not consumed by the form type itself becomes an HTML attribute on the `\u003cform\u003e` element (`id`, `class`, `data-*`, etc.).\n\n---\n\n## Authentication forms\n\n### `login`\n\nLogs in a customer.\n\n\n```liquid\n\n{% form \"login\" %}\n  \u003cinput type=\"email\" name=\"{{ form.username.name }}\" value=\"{{ form.username.value }}\"\u003e\n  \u003cinput type=\"password\" name=\"{{ form.password.name }}\"\u003e\n  \u003cbutton type=\"submit\"\u003eLog in\u003c/button\u003e\n{% endform %}\n```\n\n\n**Fields:** `username`, `password`\n\n### `register`\n\nRegisters a new customer.\n\n\n```liquid\n\n{% form \"register\" %}\n  \u003cinput name=\"{{ form.firstname.name }}\" value=\"{{ form.firstname.value }}\" placeholder=\"First name\"\u003e\n  \u003cinput name=\"{{ form.lastname.name }}\" value=\"{{ form.lastname.value }}\" placeholder=\"Last name\"\u003e\n  \u003cinput type=\"email\" name=\"{{ form.email.name }}\" value=\"{{ form.email.value }}\" placeholder=\"Email\"\u003e\n  \u003cinput type=\"password\" name=\"{{ form.password.name }}\" placeholder=\"Password\"\u003e\n  \u003cbutton type=\"submit\"\u003eCreate account\u003c/button\u003e\n{% endform %}\n```\n\n\n**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`\n\n### `sso-login`\n\nSingle Sign-On login via an external provider. Renders the SSO redirect mechanism.\n\n### `forgot-password`\n\nRequests a password reset email.\n\n**Fields:** `email`\n\n### `reset-password`\n\nSets a new password from a reset token.\n\n**Fields:** `password`, `password_confirmation`\n\n### `resend-confirmation`\n\nResends the email confirmation.\n\n**Fields:** `username`\n\n### `accept-invitation`\n\nAccepts an invitation to create an account.\n\n**Fields:** `password`, `password_confirmation`, `campaign_ids`\n\n### `account-missing-details`\n\nCollects required profile information after login when mandatory fields are missing. Only renders fields with missing values — the set of fields is dynamic.\n\n**Fields:** Varies based on what's missing from the contact profile\n\n---\n\n## Cart forms\n\n### `add-to-cart`\n\nAdds a product to the cart. Pass the product as an option (preferred) or use `product_id`.\n\n\n```liquid\n\n{% form \"add-to-cart\", product: current_product %}\n  \u003cinput type=\"number\" name=\"{{ form.quantity.name }}\" value=\"1\" min=\"1\"\u003e\n  \u003cbutton type=\"submit\"\u003eAdd to cart\u003c/button\u003e\n{% endform %}\n```\n\n\n**Options:** `product` (ProductDrop, preferred) or `product_id` (String)\n\n**Fields:** `quantity`, `price` (for variable pricing), `product_bookable_location_id`, `booking_start`, `booking_end`, plus any custom form question answers\n\n**\"Buy now\" pattern** — use `formaction` to redirect directly to checkout:\n\n\n```liquid\n\n{% form \"add-to-cart\", product: current_product %}\n  \u003cinput type=\"number\" name=\"{{ form.quantity.name }}\" value=\"1\"\u003e\n  \u003cbutton type=\"submit\"\u003eAdd to cart\u003c/button\u003e\n  \u003cinput type=\"submit\" formaction=\"{{ form.path | params: after: 'cart' }}\" value=\"Buy now\"\u003e\n{% endform %}\n```\n\n\n### `add-bundle-to-cart`\n\nAdds a bundle product with its configured components.\n\n**Fields:** `product_id`, bundle configuration fields\n\n### `add-preset-bundle`\n\nAdds a pre-configured bundle to the cart.\n\n### `cart`\n\nWraps the cart display for quantity updates. No pre-defined form fields — use raw `\u003cinput\u003e` elements with the `cart_items[ITEM_ID][quantity]` naming convention.\n\n\n```liquid\n\n{% form \"cart\" %}\n  {% for item in current_cart.items %}\n    \u003cinput type=\"number\" name=\"cart_items[{{ item.id }}][quantity]\" value=\"{{ item.quantity }}\"\u003e\n  {% endfor %}\n  \u003cbutton type=\"submit\"\u003eUpdate cart\u003c/button\u003e\n{% endform %}\n```\n\n\nTo remove a cart item, use the item's `delete_path`:\n\n\n```liquid\n\n\u003ca href=\"{{ item.delete_path }}\"\u003eRemove\u003c/a\u003e\n```\n\n\n---\n\n## Checkout forms\n\n### `checkout-customer-information`\n\nCollects customer details at the first checkout step.\n\n\n```liquid\n\n{% form \"checkout-customer-information\", id: \"SC-CheckoutShippingForm\" %}\n  {% render \"form_errors\", errors: form.errors %}\n  {% render \"checkout/customer_information/form\", form: form %}\n{% endform %}\n```\n\n\n**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\n\n### `checkout-shipping-information`\n\nSelects the shipping method.\n\n\n```liquid\n\n{% form \"checkout-shipping-information\" %}\n  {% render \"form_errors\", errors: form.errors %}\n  {% render \"checkout/shipping_information/form\", form: form %}\n{% endform %}\n```\n\n\n**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\n\n### `checkout-accept-terms`\n\nAccepts terms and conditions.\n\n\n```liquid\n\n{% form \"checkout-accept-terms\", id: \"SC-CheckoutTerms\" %}\n  {% render \"form_errors\", errors: form.errors %}\n  {% render \"checkout/accept_terms/form\", form: form %}\n{% endform %}\n```\n\n\n**Fields:** `terms_accepted`, plus custom form question answers\n\n### `payment`\n\nSubmits 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.\n\n### `payment-not-required`\n\nHandles orders that don't require payment — for example, orders fully covered by account credits or using pay-by-account.\n\n---\n\n## Voucher and promotion forms\n\n### `apply-voucher`\n\nApplies a voucher code to the cart.\n\n\n```liquid\n\n{% form \"apply-voucher\" %}\n  \u003cinput name=\"{{ form.code.name }}\" placeholder=\"Voucher code\"\u003e\n  \u003cinput name=\"{{ form.pin.name }}\" placeholder=\"PIN (if required)\"\u003e\n  \u003cbutton type=\"submit\"\u003eApply\u003c/button\u003e\n{% endform %}\n```\n\n\n**Fields:** `code`, `pin`\n\n### `remove-voucher`\n\nRemoves an applied voucher. Pass the voucher as an option.\n\n\n```liquid\n\n{% form \"remove-voucher\", voucher: voucher %}\n  \u003cbutton type=\"submit\"\u003eRemove\u003c/button\u003e\n{% endform %}\n```\n\n\n### `activate-voucher`\n\nActivates a voucher from the voucher detail page. Pass the voucher as an option.\n\n### `apply-promo-code`\n\nApplies a promotion/coupon code to the cart.\n\n\n```liquid\n\n{% form \"apply-promo-code\" %}\n  \u003cinput name=\"{{ form.code.name }}\" placeholder=\"Promo code\"\u003e\n  \u003cbutton type=\"submit\"\u003eApply\u003c/button\u003e\n{% endform %}\n```\n\n\n**Fields:** `code`\n\n### `remove-promo-code`\n\nRemoves an applied promotion code.\n\n---\n\n## Account credit forms\n\n### `apply-account-credit`\n\nApplies account credit to the order.\n\n**Fields:** `id` (credit account ID), `amount` (amount to apply)\n\n### `remove-account-credit`\n\nRemoves applied account credit. Requires `account_credit` option.\n\n---\n\n## Account management forms\n\n### `account`\n\nUpdates the customer's profile and login credentials.\n\n\n```liquid\n\n{% form \"account\" %}\n  \u003cinput name=\"{{ form.firstname.name }}\" value=\"{{ form.firstname.value }}\"\u003e\n  \u003cinput name=\"{{ form.lastname.name }}\" value=\"{{ form.lastname.value }}\"\u003e\n  \u003cinput type=\"email\" name=\"{{ form.email.name }}\" value=\"{{ form.email.value }}\"\u003e\n  \u003cinput name=\"{{ form.phone.name }}\" value=\"{{ form.phone.value }}\"\u003e\n  \u003cbutton type=\"submit\"\u003eUpdate\u003c/button\u003e\n{% endform %}\n```\n\n\n**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\n\n---\n\n## Custom forms\n\nContact 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.\n\n### `custom-form`\n\nSubmits a custom form defined in the CMS.\n\n\n```liquid\n\n{% form \"custom-form\", custom_form: my_form %}\n  {% for question in my_form.questions %}\n    \u003cdiv class=\"field\"\u003e\n      \u003clabel\u003e{{ question.label }}\u003c/label\u003e\n      {% case question.question_type %}\n      {% when \"text\" %}\n        \u003cinput type=\"text\" name=\"{{ question.input_name }}\" value=\"{{ question.answer_value }}\"\u003e\n      {% when \"text_area\" %}\n        \u003ctextarea name=\"{{ question.input_name }}\"\u003e{{ question.answer_value }}\u003c/textarea\u003e\n      {% when \"picklist\" %}\n        \u003cselect name=\"{{ question.input_name }}\"\u003e\n          {% for option in question.picklist_values %}\n            \u003coption value=\"{{ option }}\" {% if question.answer_value == option %}selected{% endif %}\u003e{{ option }}\u003c/option\u003e\n          {% endfor %}\n        \u003c/select\u003e\n      {% when \"integer\" %}\n        \u003cinput type=\"number\" name=\"{{ question.input_name }}\" value=\"{{ question.answer_value }}\"\u003e\n      {% when \"date\" %}\n        \u003cinput type=\"date\" name=\"{{ question.input_name }}\" value=\"{{ question.answer_value }}\"\u003e\n      {% when \"file\" %}\n        \u003cinput type=\"file\" name=\"{{ question.input_name }}\"\u003e\n      {% endcase %}\n    \u003c/div\u003e\n  {% endfor %}\n  \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n{% endform %}\n```\n\n\n**Supported question types:** `text`, `text_area`, `picklist`, `multi_picklist`, `integer`, `decimal`, `date`, `datetime`, `file`, `hidden`\n\n---\n\n## Subscription and additional payment forms\n\n### `checkout-set-password`\n\nCreates an account after guest checkout. Requires the `order` option.\n\n**Fields:** `password`, `campaign_ids`\n\n### `additional-payment-billing-address`\n\nUpdates the billing address for additional payment (pay-by-link). Requires the order context.\n\n**Fields:** `billing_address_lines`, `billing_city`, `billing_state`, `billing_postal_code`, `billing_country`\n\n### `subscription-payment`\n\nPays an outstanding subscription installment. Requires the `subscription` option.\n\n### `update-subscription-payment-details`\n\nUpdates the payment method on a subscription. Requires the `subscription` option.\n\n---\n\n## Booking forms\n\n### `booking-attendee-add`\n\nAdds an attendee to a booking.\n\n**Fields:** Attendee information fields (firstname, lastname, email, phone, etc.)\n\n### `booking-attendee-edit`\n\nEdits an existing booking attendee.\n\n---\n\n## Privacy forms\n\n### `privacy-accept-all`\n\nAccepts all cookie groups.\n\n### `privacy-reject-all`\n\nRejects all optional cookie groups.\n\n### `privacy-settings`\n\nSaves specific cookie preferences.\n\n---\n\n## Geolocation forms\n\n### `geolocation-select`\n\nSelects a store based on the customer's location.\n\n### `geolocation-dismiss`\n\nDismisses the store location suggestion.\n\n---\n\n## Form error handling\n\nAll forms expose validation errors when submission fails. Access errors at the form level or per field.\n\n\n```liquid\n\n{% form \"register\" %}\n  {% if form.errors.size \u003e 0 %}\n    \u003cdiv class=\"errors\"\u003e\n      \u003cul\u003e\n        {% for error in form.errors %}\n          \u003cli\u003e{{ error }}\u003c/li\u003e\n        {% endfor %}\n      \u003c/ul\u003e\n    \u003c/div\u003e\n  {% endif %}\n\n  \u003cdiv class=\"field {% if form.email.errors.size \u003e 0 %}has-error{% endif %}\"\u003e\n    \u003clabel\u003e{{ form.email.label }}\u003c/label\u003e\n    \u003cinput type=\"email\" name=\"{{ form.email.name }}\" value=\"{{ form.email.value }}\"\u003e\n    {% for error in form.email.errors %}\n      \u003cspan class=\"error\"\u003e{{ error }}\u003c/span\u003e\n    {% endfor %}\n  \u003c/div\u003e\n\n  \u003cbutton type=\"submit\"\u003eRegister\u003c/button\u003e\n{% endform %}\n```\n\n\nWhen a form submission fails:\n1. The page re-renders with the same form.\n2. `form.errors` contains all validation errors.\n3. Field values are preserved via `form.field.value`.\n4. Per-field errors are available via `form.field.errors`."}