Fluent Forms custom fields
SkillMediaBuilds and reviews third-party Fluent Forms input fields with the Free-core BaseFieldManager API. Covers fluentform/loaded bootstrap, editor component schema, frontend rendering, input-name mapping, conditional logic, server-side normalization and validation, response formatting, accessibility, assets, and Free versus Pro feature boundaries. Use when code extends BaseFieldManager, adds fluentform/render_item_* or fluentform/validate_input_item_* hooks, creates a custom field for the form builder, or must make a field appear correctly in entries, emails, feeds, and conditional rules.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Fluent Forms custom fields skill
What this skill tells your AI
The instructions your AI receives, as published by lonsdale201/wp-agent-skills in fluentform/fluentform-custom-fields/SKILL.md and read by ahel’s review.
Build fields against the documented Free-core BaseFieldManager contract. Do
not copy a Pro component and accidentally make the extension depend on Pro.
Read field-contract.md when implementing a new field or debugging nested values, response rendering, editor settings, or Pro feature detection.
Availability contract
| Surface | Availability in 6.2.7 |
|---|---|
FluentForm\App\Services\FormBuilder\BaseFieldManager | Free |
| Editor registration, frontend render hook, parser input type, conditional support | Free |
| Element input/validation/response filters | Free |
| Phone, range slider, NPS, ranking, dynamic field, chained select, repeater, rich text, file upload | Pro implementations |
The Pro fields demonstrate the same Free base class. Referencing their element keys, JavaScript, uploader, data-source, or server classes is still Pro-only.
Workflow
- Inspect the installed versions and feature-detect every class or constant used.
- Choose a globally unique, lowercase element key and a configurable input
attributes.name; never use the element key as a permanent business ID. - Bootstrap on
fluentform/loadedand instantiate the field once. - Return a complete editor component with
element,attributes,settings, andeditor_options. - Render with the inherited markup helpers so labels, error placement, conditional logic, repeated form instances, and accessibility remain intact.
- Normalize before rule validation, validate on the server, then add a separate display formatter for entries/emails.
- Test editor insertion, saved/reloaded configuration, classic and conversational rendering, valid/invalid submission, conditional visibility, entry display, email/feed value, and two instances of the same form.
Bootstrap and field skeleton
use FluentForm\App\Services\FormBuilder\BaseFieldManager;
use FluentForm\Framework\Helpers\ArrayHelper as Arr;
add_action('fluentform/loaded', static function (): void {
if (!class_exists(BaseFieldManager::class)) {
return;
}
new Acme_Order_Code_Field();
});
final class Acme_Order_Code_Field extends BaseFieldManager
{
public function __construct()
{
parent::__construct(
'acme_order_code',
__('Order code', 'acme-addon'),
['order', 'reference', 'code'],
'advanced'
);
}
public function getComponent()
{
return [
'index' => 20,
'element' => $this->key,
'attributes' => [
'type' => 'text',
'name' => 'acme_order_code',
'value' => '',
'class' => '',
'placeholder' => '',
],
'settings' => [
'label' => __('Order code', 'acme-addon'),
'admin_field_label' => '',
'label_placement' => '',
'help_message' => '',
'container_class' => '',
'validation_rules' => [
'required' => [
'value' => false,
'message' => __('This field is required.', 'acme-addon'),
],
],
'conditional_logics' => [],
],
'editor_options' => [
'title' => __('Order code', 'acme-addon'),
'icon_class' => 'ff-edit-text',
'template' => 'inputText',
],
];
}
public function render($data, $form)
{
$data['attributes']['id'] = $this->makeElementId($data, $form);
$data['attributes']['class'] = trim(
'ff-el-form-control ' . Arr::get($data, 'attributes.class', '')
);
$input = '<input ' . $this->buildAttributes($data['attributes'], $form) . '>';
$html = $this->buildElementMarkup($input, $data, $form);
$this->printContent(
'fluentform/rendering_field_html_' . $this->key,
$html,
$data,
$form
);
}
}
Keep render() output escaped. The inherited helpers escape attributes and
produce Fluent Forms-compatible wrappers; they do not make arbitrary custom HTML
or JavaScript safe.
Normalize and validate
add_filter(
'fluentform/input_data_acme_order_code',
static fn($value) => is_string($value) ? strtoupper(trim($value)) : $value,
10,
1
);
add_filter(
'fluentform/validate_input_item_acme_order_code',
static function ($error, $field, $formData, $fields, $form, $errors = []) {
$name = (string) ($field['name'] ?? '');
$value = (string) ($formData[$name] ?? '');
if ($value !== '' && !preg_match('/^[A-Z0-9-]{6,32}$/', $value)) {
$error = is_array($error) ? $error : ($error ? [$error] : []);
$error['acme_format'] = __('Use 6–32 letters, numbers, or dashes.', 'acme-addon');
}
return $error;
},
10,
6
);
Scope form-specific rules with (int) $form->id. Never rely only on browser
validation. Do not add raw request keys to fluentform/white_listed_fields merely
to make a field persist; a registered input type and a valid attributes.name
are the correct path.
Response formatting
Use response formatting only for display. Preserve the stored machine value.
add_filter(
'fluentform/response_render_acme_order_code',
static function ($response, $field, $formId, $isHtml) {
$value = (string) $response;
return $isHtml ? esc_html($value) : $value;
},
10,
4
);
Critical rules
- Treat
attributes.nameas the key connecting browser data,$formData, the submissionresponseJSON, entry details, smart codes, and feed mappings. - Keep filters pure and deterministic; rendering and formatting can occur more than once in a request.
- Register scripts/styles only when the target form contains the field and use unique handles. Do not enqueue Pro assets from a Free-only addon.
- Support arrays intentionally. A scalar-looking renderer or validator is not automatically safe for repeaters, containers, or multi-value inputs.
- Do not use the unrelated
FluentForm\App\Modules\Component\BaseComponentAPI as the default extension path;BaseFieldManageris the documented and core/Pro-used field manager in 6.2.7.
Cross-references
- Use
fluentform-submission-lifecyclefor hook timing and transformations. - Use
fluentform-entries-datafor stored response and entry-detail semantics. - Use
wp-security-auditwhen the custom renderer outputs complex HTML.
References
- Official Base Field Manager documentation: https://developers.fluentforms.com/api/classes/base-field-manager/
- Verified Free source paths:
fluentform/app/Services/FormBuilder/BaseFieldManager.phpfluentform/app/Services/FormBuilder/Components/BaseComponent.phpfluentform/app/Services/Parser/Form.phpfluentform/app/Services/Form/FormValidationService.php
- Verified Pro examples, required only when their features are used:
fluentformpro/src/Components/RangeSliderField.phpfluentformpro/src/Components/DynamicField/DynamicField.phpfluentformpro/src/Components/RepeaterField.php
Signals
- GitHub stars
- 22
- Forks
- 2
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
fluentform-custom-fields- Source
- github.com/lonsdale201/wp-agent-skills