Skip to content

form ​

Last updated View as Markdown

Outputs an HTML <form> with the inputs the storefront needs to handle it. The form works without JavaScript: the storefront handles the post and redirects back, or to return_to.

liquid
{% form 'type', object, attribute: value %}
  content
{% endform %}
  • type: the form type, one of the types below. The quotes are optional.
  • object: the object some types need, for example a product.
  • attribute: value: any HTML attribute of the <form> tag, for example id, class or data-role. Values are escaped.
  • return_to: where to go after a successful post. See Return to.

Every posted form also outputs a CSRF token, a form_type input and a hidden spam field. Do not remove them.

liquid
{% form 'contact', id: 'contact-form', class: 'contact' %}
  <input type="email" name="email">
  <button type="submit">Send</button>
{% endform %}
html
<form method="post" action="https://store.youcan.store/contact" accept-charset="UTF-8" id="contact-form" class="contact">
  <input type="hidden" name="form_type" value="contact">
  <input type="hidden" name="_token" value="CSRF_TOKEN">
  <input type="text" name="hidden_check" value="" tabindex="-1" autocomplete="off" aria-hidden="true" style="position:absolute;left:-9999px">
  <input type="email" name="email">
  <button type="submit">Send</button>
  <div data-captcha>…</div>
</form>

The form object ​

Inside a form, form describes the last post of that form type. It is empty on a normal page view.

NameTypeDescription
typestringThe form type
posted_successfullybooleanWhether this form was posted and the post succeeded
errorsform errorsThe errors of the post, or nil
valuesobjectThe submitted values when the post failed, without passwords
any field namestringThe same submitted values, for example form.email

form.errors has:

  • messages: the first message of each field, for example form.errors.messages.email.
  • translated_fields: a readable name for each field, for example Email for email.
  • Looping over form.errors gives pairs: error[0] is the field, error[1] its messages.

The default_errors filter outputs all messages as a list:

liquid
{% form 'contact' %}
  {% if form.posted_successfully %}
    <p>{{ 'contact.success' | t }}</p>
  {% endif %}
  {{ form.errors | default_errors }}
  <input type="email" name="email" value="{{ form.email }}">
{% endform %}
html
<div class="errors"><ul><li>The email field is required.</li></ul></div>

Return to ​

return_to sets where to go after a successful post. A failed post always goes back to the page of the form, so the errors can show.

  • A path on the store, for example /cart.
  • back: the page the form was on.
  • checkout: the checkout, one page or step by step, depending on the cart.

Other values, including URLs on other domains, are ignored.

liquid
{% form 'product', product, return_to: 'checkout' %}
  <input type="number" name="quantity" value="1">
  <button type="submit">{{ 'product.buy_now' | t }}</button>
{% endform %}

Types ​

product ​

Adds a product to the cart. With a product as object, the form outputs the id input of the selected or first available variant. A product with "skip to checkout" goes to the checkout after the post, unless you set return_to.

InputRequiredDescription
idyesThe variant id. Output for you when you pass a product
quantityyesThe quantity
attachedImagenoThe URL of an image the customer uploaded, for variants that need one
liquid
{% form 'product', product %}
  <input type="number" name="quantity" value="1" min="1">
  <button type="submit">{{ 'product.add_to_cart' | t }}</button>
{% endform %}

A select named id inside the form replaces the variant the form outputs.

express_checkout ​

Places an order from the product page in one step. Pass the product as object. After the post the customer goes to the thank you page, to the upsell offers, or to the shipping or payment step when the store needs them. return_to is not used.

InputRequiredDescription
quantityyesThe quantity
checkout fields, for example first_name and phoneas set by the storeThe customer details

Field errors come back in form.errors.

cart ​

Updates the cart.

InputRequiredDescription
updates[ITEM_ID]noThe new quantity of a cart item. 0 removes it
notenoThe cart note. Read it with cart.note
clearnoRemoves all items when present
checkoutnoGoes to the checkout after the update when present
liquid
{% form 'cart', cart %}
  {% for item in cart.items %}
    <input type="number" name="updates[{{ item.id }}]" value="{{ item.quantity }}" min="0">
  {% endfor %}
  <textarea name="note">{{ cart.note }}</textarea>
  <button type="submit">{{ 'cart.update' | t }}</button>
  <button type="submit" name="checkout">{{ 'cart.checkout' | t }}</button>
{% endform %}

coupon ​

Applies a coupon to the cart.

InputRequiredDescription
couponyesThe coupon code

coupon_remove ​

Removes the coupon from the cart. It needs no inputs.

product_review ​

Adds a review to a product. Pass the product as object. Reviews wait for the seller's approval.

InputRequiredDescription
ratingsyesA rating from 0 to 5
contentnoThe review text, 5 to 2000 characters
first_namenoThe reviewer's first name
last_namenoThe reviewer's last name
emailnoThe reviewer's email
images[]noImage URLs

contact ​

Sends a message to the seller. The form outputs a captcha.

InputRequiredDescription
emailyesThe customer's email
subjectyesThe subject
messageyesThe message

Customer types ​

These types handle customer accounts. Use them in the customer templates. The customer_login, create_customer, recover_customer_password, reset_customer_password and customer forms output a captcha, and the post fails without it.

customer_login ​

Logs the customer in. After the post the customer goes to return_to, or to the home page.

InputRequiredDescription
emailyesThe customer's email
passwordyesThe password
remembernoKeeps the customer logged in when checked
liquid
{% form 'customer_login', return_to: routes.account_url %}
  {{ form.errors | default_errors }}
  <input type="email" name="email" value="{{ form.email }}">
  <input type="password" name="password">
  <button type="submit">Log in</button>
{% endform %}

create_customer ​

Creates an account. After the post the customer goes to the login page. When the email belongs to a past customer without an account, the store sends an email to set the password.

InputRequiredDescription
emailyesThe customer's email
passwordyesThe password, 6 to 72 characters
password_confirmationyesThe same password
first_namenoThe first name
last_namenoThe last name
phonenoThe phone number

recover_customer_password ​

Sends a password reset email. The store sends it only when an account exists for the email, and the post succeeds in both cases.

InputRequiredDescription
emailyesThe customer's email

reset_customer_password ​

Sets a new password from the link in the reset email. Use it in the customer-reset-password template. The form takes the reset link from the page URL. After the post the customer is logged in and goes to the home page.

InputRequiredDescription
passwordyesThe new password, 6 to 72 characters
password_confirmationyesThe same password

customer_update ​

Updates the logged in customer.

InputRequiredDescription
first_nameyesThe first name, 2 or more characters
last_nameyesThe last name, 2 or more characters
emailyesThe email
phonenoThe phone number
passwordnoA new password, 6 to 72 characters
password_confirmationwith passwordThe same password
current_passwordwhen the email or the password changesThe current password
liquid
{% form 'customer_update' %}
  {{ form.errors | default_errors }}
  <input type="text" name="first_name" value="{{ customer.first_name }}">
  <input type="text" name="last_name" value="{{ customer.last_name }}">
  <input type="email" name="email" value="{{ customer.email }}">
  <input type="password" name="current_password">
  <button type="submit">Save</button>
{% endform %}

customer ​

Subscribes an email to the store's marketing emails. The customer does not need an account.

InputRequiredDescription
emailyesThe email

customer_address ​

Adds or updates an address of the logged in customer. Pass customer.new_address as object to add an address. Pass an address from customer.addresses to update it: the form outputs its id input. A customer can save up to 50 addresses.

InputRequiredDescription
first_lineyesThe first address line
cityyesThe city
country_codeyesThe country code, for example MA
first_namenoThe first name
last_namenoThe last name
companynoThe company
second_linenoThe second address line
regionnoThe region
zip_codenoThe zip code
phonenoThe phone number
defaultno1 makes it the default address
liquid
{% for address in customer.addresses %}
  {% form 'customer_address', address %}
    <input type="text" name="first_line" value="{{ address.address1 }}">
    <input type="text" name="city" value="{{ address.city }}">
    <input type="text" name="country_code" value="{{ address.country_code }}">
    <button type="submit">Save</button>
  {% endform %}
{% endfor %}

{% form 'customer_address', customer.new_address %}
  <input type="text" name="first_line">
  <input type="text" name="city">
  <input type="text" name="country_code">
  <button type="submit">Add</button>
{% endform %}

customer_address_remove ​

Removes an address. Pass the address as object. It needs no inputs.

liquid
{% form 'customer_address_remove', address %}
  <button type="submit">Remove</button>
{% endform %}