
- MODX 3
- PHP 8.2
- Vue 3


You build the field set in the inspector: text, email, phone, textarea, select, radio, checkbox, date. Set Form key and success text. Submission goes through FetchIt and the PageBuilderContactForm snippet. Do not call the handler from the template.

Информация
Requires PageBuilder Pro and FetchIt.
form_key (pb_form_key)published_jsonForm key (form_key) must be unique on the page if you have several forms.
Repeater Form fields: name, label, type (text, email, phone, textarea, select, radio, checkbox, date), required. For select and radio, write one value per line. The line becomes both the label and the value. Field name: [a-z][a-z0-9_]*, case-insensitive. A required checkbox stays empty until the value is 1, yes, true, or on.
Email recipient: emailsender or site mail settings (same as handler). Without FetchIt the section shows a form unavailability message.
| Parameter | Value |
|---|---|
| key | contact_form |
| Layer | Pro |
| Category | conversion (conversion) |
| Chunk | pagebuilderpro_contact_form |
| Requirements | pro |
The section is part of the page context (runtimeContext: true) so the HTML cache does not freeze the form. The storefront needs FetchIt (see the note above).
Field types: field types reference.
title) Type text. Optional.
intro) Type textarea. Optional.
form_key) Type text. Required.
fields) Type repeater. Required. Repeating rows. Add button in inspector.
Each row:
| Field | Type | Label | Required |
|---|---|---|---|
name | text | Field name (name) | yes |
label | text | Label | yes |
type | select | Field type | yes |
required | yesno | Required | no |
submit_label) Type text. Optional.
success_message) Type textarea. Optional.
redirect_url) Type url. Optional.
Section pb-contact-form. AJAX via FetchIt → PageBuilderContactForm. Honeypot nospam: silent success without email.
Example JSON after section save (field schema, not visitor responses):
{
"title": "Leave a request",
"intro": "We will reply during business hours.",
"form_key": "contact",
"fields": [
{
"_rowId": "00000000-0000-4000-8000-000000000001",
"name": "name",
"label": "Name",
"type": "text",
"required": true
},
{
"_rowId": "00000000-0000-4000-8000-000000000002",
"name": "email",
"label": "Email",
"type": "email",
"required": true
}
],
"submit_label": "Send",
"success_message": "Thank you! We will contact you soon.",
"redirect_url": "https://example.com/thanks"
}Fenom chunk pagebuilderpro_contact_form:
{set $formKey = $form_key|default:'contact'}
<section class="pb-section pb-section--contact-form pb-contact-form{if $cssClass} {$cssClass|escape}{/if}" data-pb-section="contact_form"{if $id} id="pb-{$id|escape}"{/if}>
<div class="pb-section__inner pb-contact-form__inner">
{if $title}
<h2 class="pb-heading pb-contact-form__title">{$title|escape}</h2>
{/if}
{if $intro}
<p class="pb-contact-form__intro">{$intro|escape}</p>
{/if}
{if !$fetchit_available}
<p class="pb-contact-form__fallback" role="status">{$lex_form_unavailable|escape}</p>
{else}
{'!PageBuilderFetchIt' | snippet : [
'snippet' => 'PageBuilderContactForm',
'form' => 'pagebuilderpro_contact_form_fields',
'form_key' => $formKey,
'resource_id' => $resource_id|default:0,
'successMessage' => ($success_message ?: $lex_success_default),
'validationErrorMessage' => $lex_validation_error,
'clearFieldsOnSuccess' => 1,
'fields' => $fields,
'form_csrf' => $form_csrf,
'submit_label' => $submit_label,
'lex_submit' => $lex_submit,
'lex_field_required' => $lex_field_required,
'lex_field_email' => $lex_field_email,
'cssClass' => $cssClass,
'id' => $id,
]}
{/if}
</div>
</section>Fenom chunk pagebuilderpro_contact_form_fields:
{set $formKey = $form_key|default:'contact'}
{set $submitText = $submit_label|default:$lex_submit}
<form class="pb-contact-form__form fetchit-form" method="post" novalidate>
<input type="hidden" name="pb_form_key" value="{$formKey|escape}" />
<input type="hidden" name="pb_csrf" value="{$form_csrf|escape}" />
<input type="hidden" name="nospam" value="" tabindex="-1" autocomplete="off" aria-hidden="true" class="pb-contact-form__honeypot" />
<div class="pb-contact-form__message pb-contact-form__message--success" data-success role="status"></div>
<div class="pb-contact-form__message pb-contact-form__message--error" data-validation-error role="alert"></div>
<div class="pb-contact-form__fields">
{foreach $fields as $field}
{set $fname = $field.name|default:''}
{if $fname == ''}{continue}{/if}
{set $flabel = $field.label|default:$fname}
{set $ftype = $field.type|default:'text'}
{set $frequired = $field.required|default:0}
{set $options = $field.options_list|default:($field.options|default:[])}
{set $fid = "pb-{$formKey|escape}-{$fname|escape}"}
<div class="pb-contact-form__field" data-custom="{$fname|escape}">
{if $ftype == 'checkbox'}
<label class="pb-contact-form__check" for="{$fid}">
<input type="checkbox" id="{$fid}" name="{$fname|escape}" value="1" {if $frequired}required aria-required="true"{/if} />
<span>{$flabel|escape}{if $frequired}<span class="pb-contact-form__required" aria-hidden="true">*</span>{/if}</span>
</label>
{elseif $ftype == 'textarea'}
<label class="pb-contact-form__label" for="{$fid}">
{$flabel|escape}{if $frequired}<span class="pb-contact-form__required" aria-hidden="true">*</span>{/if}
</label>
<textarea class="pb-contact-form__control" id="{$fid}" name="{$fname|escape}" rows="4" {if $frequired}required aria-required="true"{/if}></textarea>
{elseif $ftype == 'select'}
<label class="pb-contact-form__label" for="{$fid}">
{$flabel|escape}{if $frequired}<span class="pb-contact-form__required" aria-hidden="true">*</span>{/if}
</label>
<select class="pb-contact-form__control" id="{$fid}" name="{$fname|escape}" {if $frequired}required aria-required="true"{/if}>
<option value="">—</option>
{foreach $options as $option}
{set $ovalue = $option.value|default:$option}
{set $olabel = $option.label|default:$ovalue}
<option value="{$ovalue|escape}">{$olabel|escape}</option>
{/foreach}
</select>
{elseif $ftype == 'radio'}
<fieldset class="pb-contact-form__fieldset">
<legend>{$flabel|escape}{if $frequired}<span class="pb-contact-form__required" aria-hidden="true">*</span>{/if}</legend>
{foreach $options as $option}
{set $ovalue = $option.value|default:$option}
{set $olabel = $option.label|default:$ovalue}
<label class="pb-contact-form__check">
<input type="radio" name="{$fname|escape}" value="{$ovalue|escape}" {if $frequired}required aria-required="true"{/if} />
<span>{$olabel|escape}</span>
</label>
{/foreach}
</fieldset>
{else}
<label class="pb-contact-form__label" for="{$fid}">
{$flabel|escape}{if $frequired}<span class="pb-contact-form__required" aria-hidden="true">*</span>{/if}
</label>
<input
class="pb-contact-form__control"
id="{$fid}"
type="{if $ftype == 'email'}email{elseif $ftype == 'phone'}tel{elseif $ftype == 'date'}date{else}text{/if}"
name="{$fname|escape}"
{if $ftype == 'email'}autocomplete="email"{elseif $ftype == 'phone'}autocomplete="tel" inputmode="tel"{elseif $fname == 'name'}autocomplete="name"{/if}
{if $frequired}required aria-required="true"{/if}
/>
{/if}
<span class="pb-contact-form__error" data-error="{$fname|escape}"></span>
</div>
{/foreach}
</div>
<button class="pb-button pb-contact-form__submit" type="submit">{$submitText|escape}</button>
</form>Without FetchIt the chunk prints lexicon pagebuilder_fe_form_unavailable.