{"title":"Liquid forms guide","slug":"liquid-forms-guide","url":"https://support.storeconnect.com/articles/liquid-forms-guide","url_markdown":"https://support.storeconnect.com/articles/liquid-forms-guide.md","subtitle":null,"summary":"How to use the {% form %} tag in StoreConnect Liquid templates, covering form registration, field access patterns, validation and error display, security, flash messages, and practical examples for add-to-cart, newsletter, contact forms, and custom validation patterns.","type":"Developer_Documentation","video_url":"","keywords":"liquid forms, form tag, form fields, form validation, form errors, form drop, field access, csrf protection, custom forms, form patterns, theme development","last_modified":"2026-08-31T05:22:00+0000","body_markdown":"The `{% form %}` tag generates HTML forms with automatic submission handling, CSRF protection, field definitions, and validation error tracking. This guide explains how to register and use forms in your StoreConnect theme.\n\nFor a list of all available form types and their fields, see [Liquid forms reference](liquid-forms-reference).\n\n## When to use the form tag\nUse the `{% form %}` tag whenever you need to:\n\n- **Collect user input** — registration, login, profile updates, checkouts, custom contact forms\n- **Handle validation** — display per-field and form-level errors automatically\n- **Protect against CSRF** — the tag includes automatic CSRF token generation\n- **Preserve user input** — field values are automatically restored after validation failures\n- **Built-in form types** — use one of StoreConnect's 40+ pre-built forms for accounts, cart, checkout, payments, and more\n\nFor purely local UI interactions (opening modals, toggling tabs, showing/hiding elements), use client-side JavaScript instead.\n\n## How forms work\nWhen a form is submitted:\n\n1. StoreConnect receives the form submission and validates the data\n2. On **success**: the form submission is processed and the user is redirected or shown a success page\n3. On **failure**: the page re-renders with the same form, validation errors are populated, and user-entered values are preserved\n\nThis means every form automatically supports the re-display pattern:\n\n\n```liquid\n\n{% form \"register\" %}\n  {% if form.errors.size \u003e 0 %}\n    \u003c!-- Validation errors are automatically populated --\u003e\n  {% endif %}\n  \n  \u003c!-- Field values are automatically preserved --\u003e\n  \u003cinput name=\"{{ form.fields[\"email\"].name }}\" value=\"{{ form.fields[\"email\"].value }}\"\u003e\n{% endform %}\n```\n\n\n## Form registration and names\nStoreConnect provides pre-built forms for common workflows. These forms are registered in the platform and can be used by name:\n\n\n```liquid\n\n{% form \"login\" %}\n  \u003c!-- login form fields --\u003e\n{% endform %}\n\n{% form \"add-to-cart\", product: current_product %}\n  \u003c!-- add-to-cart form fields --\u003e\n{% endform %}\n\n{% form \"custom-form\", custom_form: my_contact_form %}\n  \u003c!-- custom form questions --\u003e\n{% endform %}\n```\n\n\nEach form type has a specific set of fields and parameters. See [Liquid forms reference](liquid-forms-reference) for the complete catalog.\n\n## Form structure and field access\n### The form drop\nInside a `{% form %}` block, the `form` drop provides access to:\n\n| Attribute | Description |\n|-----------|-------------|\n| `fields` | The form's fields, accessed by name as `form.fields[\"email\"]` |\n| `errors` | Form-level validation errors |\n| `path` | The form's submission URL |\n\nThese three are the whole drop. A field is only ever reached through `fields`.\n\n### Accessing fields\nReach every field through `fields`, with the field name as the key:\n\n\n```liquid\n\n{{ form.fields[\"email\"].value }}\n```\n\n\nA bare `form.username` is not a field. The drop has no such attribute, so the expression renders blank and the rest of the page renders normally. Nothing in the page marks the spot; `undefined method username` is reported only to the Console.\n\nThe key can be a variable, which is what makes it worth using a hash rather than named attributes:\n\n\n```liquid\n\n{% assign field_name = \"email\" %}\n{{ form.fields[field_name].value }}\n```\n\n\n### Field properties\nEach field has these properties:\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `name` | String | HTML `name` attribute for use in `\u003cinput\u003e` elements |\n| `id` | String | HTML `id` attribute (auto-generated) |\n| `value` | String | Current value (preserved after validation failure) |\n| `original_value` | String | Value before the current request |\n| `required?` | Boolean | Whether the field is required |\n| `errors` | Array | Validation error messages for this field |\n\n### Example: rendering a field\n\n```liquid\n\n\u003cdiv class=\"form-group\"\u003e\n  \u003clabel for=\"{{ form.fields[\"email\"].id }}\"\u003eEmail address\u003c/label\u003e\n  \u003cinput \n    type=\"email\"\n    id=\"{{ form.fields[\"email\"].id }}\"\n    name=\"{{ form.fields[\"email\"].name }}\"\n    value=\"{{ form.fields[\"email\"].value }}\"\n    {% if form.fields[\"email\"].required? %}required{% endif %}\u003e\n\n  {% assign email = form.fields[\"email\"] %}\n  {% if email.errors != blank %}\n    \u003cspan class=\"error\"\u003e{{ email.errors.messages | join: \"; \" }}\u003c/span\u003e\n  {% endif %}\n\u003c/div\u003e\n```\n\n\n## Validation and error display\nForms validate on submission. Where the errors live is covered in the [form tag reference](form-tag-reference); this section is about putting them on the page.\n\nTwo things shape every example below. `form.errors` holds one **FormError per field that failed**, not one per message, so displaying them takes a nested loop: iterate the errors, then iterate that error's `full_messages`. And a `FormError` is an object, not a string, so `{{ error }}` renders the drop rather than the text.\n\nThe error that belongs to the form as a whole rather than any input carries the field name `base`.\n\n### A form-level summary\n\nFilter to `base` so field problems are not repeated at the top of the form:\n\n\n```liquid\n\n{% if form.errors.size \u003e 0 %}\n  \u003cdiv class=\"alert alert-danger\" role=\"alert\"\u003e\n    \u003cul\u003e\n      {% for error in form.errors %}\n        {% if error.field == \"base\" %}\n          {% for message in error.full_messages %}\n            \u003cli\u003e{{ message }}\u003c/li\u003e\n          {% endfor %}\n        {% endif %}\n      {% endfor %}\n    \u003c/ul\u003e\n  \u003c/div\u003e\n{% endif %}\n```\n\n\n`full_messages` prefixes each message with the field name, which is what a summary wants. Use `messages` where the field is already obvious from context.\n\nThe base theme ships this as a snippet, so a theme built on it can render the whole summary in one line:\n\n\n```liquid\n\n{% render \"form_errors\", errors: form.errors %}\n```\n\n\nPass `include_fields: true` to list field errors in the summary as well.\n\n### Messages against a single input\n\nRead the field's own `errors`, testing it with `!= blank`:\n\n\n```liquid\n\n{% assign email = form.fields[\"email\"] %}\n\n\u003cdiv class=\"form-group {% if email.errors != blank %}has-error{% endif %}\"\u003e\n  \u003clabel for=\"email\"\u003eEmail\u003c/label\u003e\n  \u003cinput\n    type=\"email\"\n    id=\"email\"\n    name=\"{{ form.fields[\"email\"].name }}\"\n    value=\"{{ form.fields[\"email\"].value }}\"\n    {% if email.errors != blank %}aria-invalid=\"true\"{% endif %}\u003e\n  {% if email.errors != blank %}\n    \u003csmall class=\"error-feedback\"\u003e{{ email.errors.messages | join: \"; \" }}\u003c/small\u003e\n  {% endif %}\n\u003c/div\u003e\n```\n\n\n`form.fields[\"email\"].errors` is a single FormError rather than a list, so `!= blank` is the test; the `.size` that works on `form.errors` raises here.\n\nShow both levels together. A summary alone leaves the customer hunting for which input is wrong, and messages on inputs alone bury a failure that belongs to no single field.\n\n## Security considerations\n### CSRF protection\nThe `{% form %}` tag automatically includes a hidden CSRF token field. This token is required for form submission and protects against cross-site request forgery attacks. You do not need to add it manually; the tag handles this automatically.\n\n### Sensitive data\nNever expose sensitive data in:\n- HTML attributes (use `form.fields[\"field\"].value` inside `\u003cinput\u003e` elements, not in data attributes)\n- Client-side JavaScript (which can be logged or leaked)\n- Form field names (avoid custom names that reveal intent)\n\n### Field value preservation\nWhen a form fails validation and re-renders, user-entered values are restored. For sensitive fields like passwords, StoreConnect does **not** preserve the value, so users must re-enter it. This is intentional for security.\n\n\n```liquid\n\n\u003c!-- Acceptable: regular field values are preserved --\u003e\n\u003cinput name=\"{{ form.fields[\"email\"].name }}\" value=\"{{ form.fields[\"email\"].value }}\"\u003e\n\n\u003c!-- Password fields: value is NOT preserved for security --\u003e\n\u003cinput type=\"password\" name=\"{{ form.fields[\"password\"].name }}\"\u003e\n\u003c!-- form.fields[\"password\"].value is empty --\u003e\n```\n\n\n## Flash messages\nFlash messages (notices and alerts) can be displayed after a form submission redirects. In a controller, use the `{% redirect %}` tag with `notice` or `alert` options:\n\n\n```liquid\n\n{% after %}\n  {% if form.fields[\"valid?\"] %}\n    {% redirect to: \"/account\", notice: \"Profile updated successfully\" %}\n  {% endif %}\n{% endafter %}\n```\n\n\nOn the target page, display flash messages from the current request:\n\n\n```liquid\n\n{% if current_request.flash.notice %}\n  \u003cdiv class=\"alert alert-success\"\u003e{{ current_request.flash.notice }}\u003c/div\u003e\n{% endif %}\n\n{% if current_request.flash.alert %}\n  \u003cdiv class=\"alert alert-danger\"\u003e{{ current_request.flash.alert }}\u003c/div\u003e\n{% endif %}\n```\n\n\n## Common patterns\n### Pattern 1: simple login form\n\n```liquid\n\n{% form \"login\" %}\n  \u003cdiv class=\"form-group\"\u003e\n    \u003clabel for=\"username\"\u003eUsername or Email\u003c/label\u003e\n    \u003cinput \n      type=\"text\"\n      id=\"username\"\n      name=\"{{ form.fields[\"username\"].name }}\"\n      value=\"{{ form.fields[\"username\"].value }}\"\n      required\u003e\n  \u003c/div\u003e\n\n  \u003cdiv class=\"form-group\"\u003e\n    \u003clabel for=\"password\"\u003ePassword\u003c/label\u003e\n    \u003cinput \n      type=\"password\"\n      id=\"password\"\n      name=\"{{ form.fields[\"password\"].name }}\"\n      required\u003e\n  \u003c/div\u003e\n\n  {% render \"form_errors\", errors: form.errors %}\n\n  \u003cbutton type=\"submit\" class=\"btn btn-primary\"\u003eLog in\u003c/button\u003e\n  \u003ca href=\"/auth/sign_up\"\u003eCreate account\u003c/a\u003e\n{% endform %}\n```\n\n\n### Pattern 2: product add-to-cart with variants\n\n```liquid\n\n{% form \"add-to-cart\", product: current_product, id: \"AddToCartForm\" %}\n  {% if current_product.has_variants? %}\n    {% for option in current_product.options %}\n      \u003cdiv class=\"form-group\"\u003e\n        \u003clabel for=\"option_{{ option.position }}\"\u003e{{ option.name }}\u003c/label\u003e\n        \u003cselect id=\"option_{{ option.position }}\" name=\"option{{ option.position }}\"\u003e\n          \u003coption value=\"\"\u003eSelect {{ option.name }}\u003c/option\u003e\n          {% for value in option.values %}\n            \u003coption value=\"{{ value }}\"\u003e{{ value }}\u003c/option\u003e\n          {% endfor %}\n        \u003c/select\u003e\n      \u003c/div\u003e\n    {% endfor %}\n  {% endif %}\n\n  \u003cdiv class=\"form-group\"\u003e\n    \u003clabel for=\"quantity\"\u003eQuantity\u003c/label\u003e\n    \u003cinput \n      type=\"number\"\n      id=\"quantity\"\n      name=\"{{ form.fields[\"quantity\"].name }}\"\n      value=\"{{ form.fields[\"quantity\"].value | default: 1 }}\"\n      min=\"1\"\n      max=\"{{ current_product.inventory_count }}\"\u003e\n  \u003c/div\u003e\n\n  {% render \"form_errors\", errors: form.errors %}\n\n  \u003cbutton type=\"submit\" class=\"btn btn-success btn-lg\"\u003eAdd to Cart\u003c/button\u003e\n{% endform %}\n```\n\n\n### Pattern 3: newsletter signup\n\n```liquid\n\n{% form \"custom-form\", custom_form: newsletter_form %}\n  \u003cdiv class=\"form-group\"\u003e\n    \u003clabel for=\"email\"\u003eEmail\u003c/label\u003e\n    \u003cinput \n      type=\"email\"\n      id=\"email\"\n      name=\"{{ form.fields[\"email\"].name }}\"\n      value=\"{{ form.fields[\"email\"].value }}\"\n      placeholder=\"your@email.com\"\n      required\u003e\n  \u003c/div\u003e\n\n  \u003cdiv class=\"form-group form-check\"\u003e\n    \u003cinput \n      type=\"checkbox\"\n      id=\"privacy\"\n      name=\"{{ form.fields[\"privacy_consent\"].name }}\"\n      value=\"yes\"\n      required\u003e\n    \u003clabel for=\"privacy\"\u003e\n      I agree to receive marketing emails\n    \u003c/label\u003e\n  \u003c/div\u003e\n\n  {% render \"form_errors\", errors: form.errors %}\n\n  \u003cbutton type=\"submit\" class=\"btn btn-primary\"\u003eSubscribe\u003c/button\u003e\n{% endform %}\n```\n\n\n### Pattern 4: contact form with dynamic questions\n\n```liquid\n\n{% if contact_form %}\n  {% form \"custom-form\", custom_form: contact_form, id: \"ContactFormSection\" %}\n    {% render \"form_errors\", errors: form.errors %}\n\n    {% for question in contact_form.questions %}\n      \u003cdiv class=\"form-group {% if question.is_required %}required{% endif %}\"\u003e\n        \u003clabel for=\"q_{{ question.id }}\"\u003e\n          {{ question.label }}\n          {% if question.is_required %}\u003cspan class=\"text-danger\"\u003e*\u003c/span\u003e{% endif %}\n        \u003c/label\u003e\n\n        {% case question.question_type %}\n        {% when \"text\" %}\n          \u003cinput \n            type=\"text\"\n            id=\"q_{{ question.id }}\"\n            name=\"{{ question.input_name }}\"\n            value=\"{{ question.answer_value }}\"\n            {% if question.is_required %}required{% endif %}\u003e\n\n        {% when \"text_area\" %}\n          \u003ctextarea\n            id=\"q_{{ question.id }}\"\n            name=\"{{ question.input_name }}\"\n            rows=\"5\"\n            {% if question.is_required %}required{% endif %}\u003e{{ question.answer_value }}\u003c/textarea\u003e\n\n        {% when \"picklist\" %}\n          \u003cselect \n            id=\"q_{{ question.id }}\"\n            name=\"{{ question.input_name }}\"\n            {% if question.is_required %}required{% endif %}\u003e\n            \u003coption value=\"\"\u003e-- Select one --\u003c/option\u003e\n            {% for option in question.picklist_values %}\n              \u003coption value=\"{{ option }}\" {% if question.answer_value == option %}selected{% endif %}\u003e\n                {{ option }}\n              \u003c/option\u003e\n            {% endfor %}\n          \u003c/select\u003e\n\n        {% when \"multi_picklist\" %}\n          \u003cfieldset\u003e\n            {% for option in question.picklist_values %}\n              \u003clabel class=\"checkbox\"\u003e\n                \u003cinput \n                  type=\"checkbox\"\n                  name=\"{{ question.input_name }}[]\"\n                  value=\"{{ option }}\"\n                  {% if question.answer_values contains option %}checked{% endif %}\u003e\n                {{ option }}\n              \u003c/label\u003e\n            {% endfor %}\n          \u003c/fieldset\u003e\n\n        {% when \"date\" %}\n          \u003cinput \n            type=\"date\"\n            id=\"q_{{ question.id }}\"\n            name=\"{{ question.input_name }}\"\n            value=\"{{ question.answer_value }}\"\n            {% if question.is_required %}required{% endif %}\u003e\n\n        {% when \"integer\" %}\n          \u003cinput \n            type=\"number\"\n            id=\"q_{{ question.id }}\"\n            name=\"{{ question.input_name }}\"\n            value=\"{{ question.answer_value }}\"\n            {% if question.is_required %}required{% endif %}\u003e\n        {% endcase %}\n      \u003c/div\u003e\n    {% endfor %}\n\n    \u003cbutton type=\"submit\" class=\"btn btn-primary\"\u003eSend\u003c/button\u003e\n  {% endform %}\n{% endif %}\n```\n\n\n### Pattern 5: registration with address fields\n\n```liquid\n\n{% form \"register\", id: \"RegisterForm\" %}\n  \u003cfieldset\u003e\n    \u003clegend\u003eAccount Information\u003c/legend\u003e\n    \n    \u003cdiv class=\"row\"\u003e\n      \u003cdiv class=\"col-md-6\"\u003e\n        \u003cdiv class=\"form-group\"\u003e\n          \u003clabel for=\"firstname\"\u003eFirst name\u003c/label\u003e\n          \u003cinput \n            type=\"text\"\n            id=\"firstname\"\n            name=\"{{ form.fields[\"firstname\"].name }}\"\n            value=\"{{ form.fields[\"firstname\"].value }}\"\n            {% if form.fields[\"firstname\"].required? %}required{% endif %}\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"col-md-6\"\u003e\n        \u003cdiv class=\"form-group\"\u003e\n          \u003clabel for=\"lastname\"\u003eLast name\u003c/label\u003e\n          \u003cinput \n            type=\"text\"\n            id=\"lastname\"\n            name=\"{{ form.fields[\"lastname\"].name }}\"\n            value=\"{{ form.fields[\"lastname\"].value }}\"\n            {% if form.fields[\"lastname\"].required? %}required{% endif %}\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n\n    \u003cdiv class=\"form-group\"\u003e\n      \u003clabel for=\"email\"\u003eEmail address\u003c/label\u003e\n      \u003cinput \n        type=\"email\"\n        id=\"email\"\n        name=\"{{ form.fields[\"email\"].name }}\"\n        value=\"{{ form.fields[\"email\"].value }}\"\n        {% if form.fields[\"email\"].required? %}required{% endif %}\u003e\n      {% assign email = form.fields[\"email\"] %}\n      {% if email.errors != blank %}\n        \u003csmall class=\"form-text text-danger\"\u003e{{ email.errors.messages | join: \"; \" }}\u003c/small\u003e\n      {% endif %}\n    \u003c/div\u003e\n\n    \u003cdiv class=\"form-group\"\u003e\n      \u003clabel for=\"password\"\u003ePassword\u003c/label\u003e\n      \u003cinput \n        type=\"password\"\n        id=\"password\"\n        name=\"{{ form.fields[\"password\"].name }}\"\n        {% if form.fields[\"password\"].required? %}required{% endif %}\u003e\n    \u003c/div\u003e\n  \u003c/fieldset\u003e\n\n  \u003cfieldset\u003e\n    \u003clegend\u003eBilling Address\u003c/legend\u003e\n    \n    \u003cdiv class=\"form-group\"\u003e\n      \u003clabel for=\"address_lines\"\u003eStreet Address\u003c/label\u003e\n      \u003cinput \n        type=\"text\"\n        id=\"address_lines\"\n        name=\"{{ form.fields[\"billing_address_lines\"].name }}\"\n        value=\"{{ form.fields[\"billing_address_lines\"].value }}\"\u003e\n    \u003c/div\u003e\n\n    \u003cdiv class=\"row\"\u003e\n      \u003cdiv class=\"col-md-6\"\u003e\n        \u003cdiv class=\"form-group\"\u003e\n          \u003clabel for=\"city\"\u003eCity\u003c/label\u003e\n          \u003cinput \n            type=\"text\"\n            id=\"city\"\n            name=\"{{ form.fields[\"billing_city\"].name }}\"\n            value=\"{{ form.fields[\"billing_city\"].value }}\"\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"col-md-2\"\u003e\n        \u003cdiv class=\"form-group\"\u003e\n          \u003clabel for=\"state\"\u003eState\u003c/label\u003e\n          \u003cinput \n            type=\"text\"\n            id=\"state\"\n            name=\"{{ form.fields[\"billing_state\"].name }}\"\n            value=\"{{ form.fields[\"billing_state\"].value }}\"\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n      \u003cdiv class=\"col-md-4\"\u003e\n        \u003cdiv class=\"form-group\"\u003e\n          \u003clabel for=\"postal_code\"\u003ePostal code\u003c/label\u003e\n          \u003cinput \n            type=\"text\"\n            id=\"postal_code\"\n            name=\"{{ form.fields[\"billing_postal_code\"].name }}\"\n            value=\"{{ form.fields[\"billing_postal_code\"].value }}\"\u003e\n        \u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/fieldset\u003e\n\n  {% render \"form_errors\", errors: form.errors %}\n\n  \u003cbutton type=\"submit\" class=\"btn btn-primary\"\u003eCreate Account\u003c/button\u003e\n{% endform %}\n```\n\n\n### Pattern 6: \"Buy Now\" button (redirect to checkout)\nFor add-to-cart forms, you can use `formaction` on a submit button to redirect directly to checkout after adding to the cart:\n\n\n```liquid\n\n{% form \"add-to-cart\", product: current_product %}\n  \u003cdiv class=\"form-group\"\u003e\n    \u003clabel for=\"quantity\"\u003eQuantity\u003c/label\u003e\n    \u003cinput \n      type=\"number\"\n      id=\"quantity\"\n      name=\"{{ form.fields[\"quantity\"].name }}\"\n      value=\"{{ form.fields[\"quantity\"].value | default: 1 }}\"\n      min=\"1\"\u003e\n  \u003c/div\u003e\n\n  \u003cdiv class=\"button-group\"\u003e\n    \u003cbutton type=\"submit\" class=\"btn btn-outline\"\u003eAdd to Cart\u003c/button\u003e\n    \n    \u003c!-- \"Buy Now\" button redirects to checkout after adding to cart --\u003e\n    \u003cbutton \n      type=\"submit\" \n      formaction=\"{{ form.path | params: after: 'checkout' }}\"\n      class=\"btn btn-primary\"\u003e\n      Buy Now\n    \u003c/button\u003e\n  \u003c/div\u003e\n{% endform %}\n```\n\n\nThe `form.path | params: after: 'checkout'` filter modifies the form's submission URL to include a redirect parameter.\n\n## Handling custom form questions\nWhen using the `custom-form` type, you iterate over the form's questions and render fields based on their type:\n\n\n```liquid\n\n{% form \"custom-form\", custom_form: my_form %}\n  {% for question in my_form.questions %}\n    {% case question.question_type %}\n    {% when \"text\" %}\n      \u003cinput type=\"text\" name=\"{{ question.input_name }}\"\u003e\n    \n    {% when \"text_area\" %}\n      \u003ctextarea name=\"{{ question.input_name }}\"\u003e\u003c/textarea\u003e\n    \n    {% when \"picklist\" %}\n      \u003cselect name=\"{{ question.input_name }}\"\u003e\n        {% for option in question.picklist_values %}\n          \u003coption\u003e{{ option }}\u003c/option\u003e\n        {% endfor %}\n      \u003c/select\u003e\n    \n    {% when \"date\" %}\n      \u003cinput type=\"date\" name=\"{{ question.input_name }}\"\u003e\n    {% endcase %}\n  {% endfor %}\n{% endform %}\n```\n\n\nEach question has:\n- `label` — the question's display text\n- `input_name` — the form field name to use\n- `question_type` — the type of input (text, text_area, picklist, multi_picklist, integer, decimal, date, datetime, file, hidden)\n- `picklist_values` — available options (for picklist and multi_picklist)\n- `answer_value` — the user's previous answer (if re-rendering after validation)\n- `is_required` — whether the field is required\n\n## Advanced: reusable form components\nTo keep your theme DRY, extract form fields into reusable components:\n\n\n```liquid\n\n{%- comment -%}\ncomponents/form_field.liquid\n\nParameters:\n- form: the form drop\n- field_name: the name of the field to render\n- type: input type (default: \"text\")\n{%- endcomment -%}\n\n{% assign field = form.fields[field_name] %}\n\u003cdiv class=\"form-group {% if field.errors != blank %}has-error{% endif %}\"\u003e\n  \u003clabel for=\"{{ form.fields[field_name].id }}\"\u003e\n    {{ field_label }}{%- comment -%} supply your own label text; fields expose no label {%- endcomment -%}\n    {% if form.fields[field_name].required? %}\u003cspan class=\"required\"\u003e*\u003c/span\u003e{% endif %}\n  \u003c/label\u003e\n  \n  \u003cinput \n    type=\"{{ type }}\"\n    id=\"{{ form.fields[field_name].id }}\"\n    name=\"{{ form.fields[field_name].name }}\"\n    value=\"{{ form.fields[field_name].value }}\"\n    {% if form.fields[field_name].required? %}required{% endif %}\n    class=\"{% if field.errors != blank %}is-invalid{% endif %}\"\u003e\n  \n  {% if field.errors != blank %}\n    \u003csmall class=\"error-message\"\u003e{{ field.errors.messages | join: \"; \" }}\u003c/small\u003e\n  {% endif %}\n\u003c/div\u003e\n```\n\n\nThen use it in your forms:\n\n\n```liquid\n\n{% form \"register\" %}\n  {% render \"form_field\", form: form, field_name: \"firstname\" %}\n  {% render \"form_field\", form: form, field_name: \"email\", type: \"email\" %}\n  {% render \"form_field\", form: form, field_name: \"password\", type: \"password\" %}\n  \u003cbutton type=\"submit\"\u003eRegister\u003c/button\u003e\n{% endform %}\n```\n"}