Form Builder
The form builder provides the most flexibility for creating custom forms.
Basic Usage
Section titled “Basic Usage”A form on a public page needs the admin asset helpers in the layout for its script and styles — see What a public form needs.
{# Create a form builder instance #}{% set form = cms.form.builder('mycollection') %}
{# Add fields to the form #}{% do form.addField('title') %}{% do form.addField('content', {field: 'styledtext'}) %}{% do form.addField('date') %}
{# Build and render the form #}{{ form.build() }}Advanced Example
Section titled “Advanced Example”{# Create a complex form with custom layout #}{% set form = cms.form.builder('products', { id: 'my-product-id', hideID: false, save: 'Save Product', delete: 'Delete Product'}) %}
{# Build form content in columns #}{% set col1 = form.field('id') %}{% set col1 = col1 ~ form.field('name') %}{% set col1 = col1 ~ form.field('description', {field: 'styledtext'}) %}{% set col1 = col1 ~ form.field('price', {field: 'number'}) %}
{% set col2 = form.field('category', {field: 'select'}) %}{% set col2 = col2 ~ form.field('tags', {field: 'list'}) %}{% set col2 = col2 ~ form.field('featured', {field: 'toggle'}) %}{% set col2 = col2 ~ form.field('image') %}
{# Create two-column layout #}{% set layout = form.layout2Columns(col1, col2) %}
{# Build form with custom layout #}{{ form.build(layout) }}Form Buttons
Section titled “Form Buttons”{# Standalone buttons #}{{ cms.form.save('Save Changes') }}{{ cms.form.delete('Remove Item') }}Simple Forms
Section titled “Simple Forms”For basic form submission without full object management:
{# Create a simple form that posts to a route #}{{ cms.form.simple('/api/contact', '<input name="email" type="email" required>', { method: 'POST', label: 'Send Message', refresh: true}) }}Simple Form Options
Section titled “Simple Form Options”{{ cms.form.simple('/api/contact', content, { method: 'POST', # HTTP method (string, default: 'POST') label: 'Send Message', # Submit button label (string) refresh: true, # Refresh page after submission (bool, default: false) class: 'contact-form', # CSS classes (string, default: '') csrfManager: csrfManager # CSRF token manager instance}) }}Fieldsets
Section titled “Fieldsets”Use cms.form.fieldset() to group fields inside a styled fieldset container. Capture your rendered fields with a {% set %} block and pass them as the content:
{% set inner %} {{ cms.form.field("text", "first_name", { label: "First name" }) }} {{ cms.form.field("text", "last_name", { label: "Last name" }) }}{% endset %}
{{ cms.form.fieldset("Contact", inner, { formgrid: "first_name last_name" }) }}Fieldset Options
Section titled “Fieldset Options”- legend (string, optional): The fieldset legend text. Leave empty for no legend.
- content (string): Pre-rendered field HTML (typically captured with
{% set %}). - options (object, optional):
formgrid(string): Inner grid layout string using the same row syntax as schemaformgriddefinitions.class(string): Extra CSS classes to add to the fieldset element.
Example with Inner Grid
Section titled “Example with Inner Grid”{% set contact_fields %} {{ cms.form.field("text", "email", { label: "Email" }) }} {{ cms.form.field("text", "phone", { label: "Phone" }) }}{% endset %}
{{ cms.form.fieldset("Contact", contact_fields, { formgrid: "email phone", class: "contact-section"}) }}This produces the same .form-grid-fieldset markup as the schema [[ ]] syntax.
Accordions
Section titled “Accordions”Use cms.form.accordion() to group fields into collapsible panels. Capture each
panel’s fields with a {% set %} block and pass them as a list:
{% set content_fields %} {{ cms.form.field("styledtext", "body", { label: "Body" }) }}{% endset %}
{% set seo_fields %} {{ cms.form.field("text", "seoTitle", { label: "SEO Title" }) }}{% endset %}
{{ cms.form.accordion([ { title: "Content", content: content_fields, formgrid: "body body" }, { title: "SEO", content: seo_fields }]) }}Accordion Options
Section titled “Accordion Options”- panels (list): One object per panel.
title(string, optional): The panel’s summary text. Defaults toSection N.content(string): Pre-rendered field HTML (typically captured with{% set %}).formgrid(string, optional): Inner grid layout for that panel, using the same row syntax as schemaformgriddefinitions.
- options (object, optional):
class(string): Extra CSS classes to add to the group wrapper.
Open State
Section titled “Open State”A single panel renders closed. Two or more panels render with the first open and
only one open at a time. This is the same rule the schema >> << syntax follows,
and it is derived from the number of panels — there is no option for it.
This produces the same .formgrid-accordion markup as the schema >> << syntax.
Blog Form Options
Section titled “Blog Form Options”{{ cms.form.blog({ collection: 'blog', save: 'Save Post', delete: 'Delete Post', fields: { date: true, summary: true, content: true, author: true, tags: true, featured: true, draft: true, image: true, categories: false, extra: false, extra2: false, media: false, genre: false, labels: false, archived: false, gallery: false }}) }}