EXT:form
If EXT: is enabled in the TYPO3 instance, EXT: produces
JSON form definitions instead of HTML. Forms designed in the form
editor work out of the box; this page documents the headless-specific
hooks that help frontend developers.
Note
On a MIXED-mode site (headless: 2) the JSON form definition — and
every submission — requires exactly Accept: application/ as
the first Accept header value; anything else renders HTML.
Configuration (YAML)
All options live in the form's YAML configuration file.
i18n strings
Add translated UI strings (button labels, help messages) directly in
the form root. They land in the response's i18n section.
i18n:
identifier: 'i18n'
properties:
someButtonLabel: 'Submit or Cancel'
someHelpMessage: 'You need to fill out this form'
requiredFields: 'These fields are required'
Strings are translated through the standard TYPO3 XLF pipeline.
Translation keys resolve against the form's original identifier
(kept in rendering), not the runtime
identifier that headless suffixes with the content element uid — key
your XLF entries on the identifier from the form YAML. A forced
locale can be passed to Form
as its optional fourth parameter.
On key collision, i18n. entries win over the
rendering shortcut — both end up in the
response's i18n object, YAML properties last.
Form decorator
Headless ships Form as the default. Override per
form via rendering:
renderingOptions:
formDecorator: Your\Vendor\Form\CustomDefinitionDecorator
See Customising the JSON output (decorators) below for writing your own decorator.
Rich-text fields (TYPO3 v14.2+)
TYPO3 v14.2 allows RTE content in form element labels and Static
elements. To ship that HTML safely in the JSON definition, opt in per form
to the shipped decorator:
renderingOptions:
formDecorator: FriendsOfTYPO3\Headless\Form\Decorator\RichTextFormDefinitionDecorator
HTML-carrying labels and static texts are then run through
lib. (resolving t3:// links), falling back to
lib. and finally to the plain HTML sanitizer when
neither lib exists, and flagged with label/text
so the frontend knows to render them as markup. The decorator also
processes api. the same way and flags it
with message.
Validator error codes
Per validator, set error to a TYPO3 XLF error code — the
default form translation files resolve it:
validators:
- identifier: 'NotEmpty'
errorMessage: 1221560910
For Regular validators, PHP and JS regex flavours differ.
Provide a JS-flavoured fallback via FERegular:
validators:
- identifier: 'RegularExpression'
options:
regularExpression: '/^[a-z]+$/'
FERegularExpression:
expression: '^[a-z]+$'
flags: 'i'
errorMessage: 1221565130
When the headless decorator sees FERegular, it replaces
options. with that value as-is in the JSON
response — the {expression, flags} pair maps directly to JavaScript's
new Reg.
Custom options (dynamic select/radio/checkbox)
Implement Friends and
point the field at it:
- type: 'SingleSelectWithCountryList'
identifier: 'country'
label: 'Country'
properties:
customOptions: 'Your\Vendor\Form\CountryOptions'
Country is called per render and its return value
replaces the field's options. Although the interface only declares
get, implementations are instantiated with four constructor
arguments — ($field, $form: the
field's definition array, all fields of the current page, the runtime
form identifier and the Form — so options can depend on the
surrounding form state.
If your custom form type isn't a standard one (so the frontend
wouldn't know what to render), override the type sent to the
frontend with FEOverride:
type: 'SingleSelectWithCountryList'
renderingOptions:
FEOverrideType: 'Select'
JSON redirect finisher
The standard Redirect issues a real HTTP redirect. In
headless mode that breaks the SPA flow. Use the Json
finisher instead — it puts the redirect target into
api. and lets the frontend decide what to do.
Since 5.0 the finisher is registered out of the box (form set
friendsoftypo3/,
EXT:) — use it
directly in your form definition:
finishers:
-
identifier: JsonRedirect
options:
pageUid: '2'
message: 'Thanks! You will be redirected shortly.'
On success it emits { redirect
(message defaults to null, status to 303 and is also an
option). The redirect is made relative only when its host already
equals the site's frontend host; otherwise the absolute
TYPO3-host URL is returned unchanged — it is not rewritten to
frontend. It does not redirect by itself. Further
options: additional (appended to the target URL) and
same — with it, a page outside the current site (or an
unresolvable one) falls back to the finisher's default target
(page) instead of being used.
Submitting the form
POST the form to the content element's link value — headless builds
it with tx_ and
tx_ already appended.
Nothing inside the form definition itself is a valid submit target.
Every field is submitted under its exact name,
tx_. Besides the visible
fields, the JSON elements contain Hidden elements that must be
posted back verbatim:
__— the HMAC-protected form state; round-trip it unchanged.state __— the page index being submitted.current Page __— the extbase property-mapping token, generated for the exact set of listed field names: omitting any field (the honeypot included) fails property mapping.trusted Properties __— present only once the form is performing (after the first POST); echo it back on subsequent steps.session
Honeypot
When rendering is true, an extra field with a
session-random identifier appears in elements and its name is
baked into __. Render it hidden from humans and
submit it empty — filling or omitting it fails the submission.
When using a custom honeypot element, its type must match
rendering (default Honeypot) for
headless to expose it correctly in the JSON definition.
Customising the JSON output (decorators)
EXT:headless provides three building blocks:
Friends— default implementation.Of TYPO3Headless Form Decorator Form Definition Decorator Friends— base class with hooks to override per-element or whole-form output.Of TYPO3Headless Form Decorator Abstract Form Definition Decorator Friends— the contract a custom decorator implements.Of TYPO3Headless Form Decorator Definition Decorator Interface
Default output (Form) — the decorator returns the
bare definition; in the page response it sits under the content
element's content. key:
{
"id": "ContactForm-1",
"api": {
"status": null,
"errors": null,
"actionAfterSuccess": null,
"page": { "current": 0, "nextPage": null, "pages": 1 }
},
"i18n": { "submitButtonLabel": "Submit" },
"elements": []
}
elements is abbreviated here — in a real response it lists the
current page's fields plus the hidden round-trip fields described in
Submitting the form above.
Subclass Abstract if you only need to tweak
one element type or one form root field; implement
Definition directly if you want full control.
Attach via rendering as shown above.