Form
View as MarkdownA powerful form component with client-side validation, AJAX submission, and seamless Extbase integration.
Features
Permalink to heading "Features"- Optional client-side validation with Standard Schema-compatible validators or custom callbacks
- AJAX form submission with automatic error handling
- Seamless Extbase controller integration
- Field validation that follows the validation mode, from native constraints, your schema or callback, and async checks of single fields
- Form state management (submitting, dirty, invalid, success, error)
- Built-in primitives for editable content, state indicators, and status text
- Works with all Field-aware components and basic HTML inputs
- Automatic field name prefixing for Extbase
See the complete Form Guide for a complete form integration.
<primitives:form.root>
<primitives:form.content>
<primitives:field.root>
<primitives:field.label />
<primitives:field.control asChild="{true}">
<!-- Your form input here -->
</primitives:field.control>
<primitives:field.errorText />
</primitives:field.root>
</primitives:form.content>
<primitives:form.indicator
state="{f:constant(name: 'Jramke\FluidPrimitives\Enum\FormState::Error')}"
>
<primitives:form.errorText />
</primitives:form.indicator>
</primitives:form.root>
Installation
Permalink to heading "Installation"typo3 ui:add form
Please copy the files manually from GitHub into your project.
Read more about installing Components and Primitives.
Client-side validation is configured in your entry file with the validation option, not in the Fluid template. Pass either a Standard Schema-compatible validator such as Zod or a synchronous callback that reads the values object. values uses your field names as dot paths such as person.name, keeps leaf values as string | File, exposes arrays through getAll(), and can be converted to a nested object with toObject() or partially read with pick(). Validation and server errors stay flat and field-keyed, so nested fields still return errors under keys such as person.name. For submit results, return true, false, or field errors from onSubmit, and use api.setErrorText() or api.setSuccessText() for form-level messages. Use post(url) to submit the current form as FormData. Fields read their value inputDebounceMs after the last change (100 by default, never less than 50), so typing does not validate on every keystroke.
API Reference
Permalink to heading "API Reference"The following tables cover the available props of the Fluid Primitives.
form.root
Permalink to heading "form.root"Submits and manages the form state. Renders a <form> element.
| Name | Description | Required | Default |
|---|---|---|---|
actionUri | stringThe resolved form action URI to submit to directly. | No | - |
action | stringThe Extbase action name used to build the form action URI. | No | - |
extensionName | stringThe Extbase extension name used to build the form action URI. | No | - |
pluginName | stringThe Extbase plugin name used to build the form action URI. | No | - |
controller | stringThe Extbase controller name used to build the form action URI. | No | - |
arguments | arrayThe Extbase arguments used to build the form action URI. | No | - |
pageUid | intThe target page UID used to build the form action URI. | No | - |
objectName | stringThe object name prefix used for nested form field names. | No | - |
object | mixedThe bound object used for form value mapping. | No | - |
method | stringThe HTTP method used for form submission. | No | 'post' |
attributes | arrayAdditional attributes that should be rendered on the component where ui:attributes is used. | No | [] |
asChild | booleanIf true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. | No | - |
rootId | stringThe root ID of the component, used for hydration and identification. | No | - |
ids | arrayThe IDs of of the component parts for composition. | No | [] |
autoMount | booleanWhether the client initializes the component on its own. Set it to false to mount it yourself with `mount()` | No | true |
class | stringThe CSS class(es) to be applied to the component. | No | - |
Rendered data attributes
| Attribute | Description |
|---|---|
data-state | The state of the form. One of: "invalid", "ready", "submitting", "success", "error". |
data-submitting | Present while the form is being submitted. |
data-invalid | Present when invalid |
data-dirty | Present when dirty |
data-touched | Present when touched |
form.content
Permalink to heading "form.content"Wraps the editable form UI. It stays visible in ready, invalid, and submitting, and hides in error and success. Renders a <div> element.
| Name | Description | Required | Default |
|---|---|---|---|
attributes | arrayAdditional attributes that should be rendered on the component where ui:attributes is used. | No | [] |
asChild | booleanIf true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. | No | - |
class | stringThe CSS class(es) to be applied to the component. | No | - |
form.indicator
Permalink to heading "form.indicator"Displays content for an exact form state such as error, success, or submitting. Renders a <div> element.
| Name | Description | Required | Default |
|---|---|---|---|
state | Enum\FormStateThe form state to render. | Yes | - |
attributes | arrayAdditional attributes that should be rendered on the component where ui:attributes is used. | No | [] |
asChild | booleanIf true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. | No | - |
class | stringThe CSS class(es) to be applied to the component. | No | - |
form.errorText
Permalink to heading "form.errorText"Displays the current form-level error text set through the Form API, or its slotted fallback text. Renders a <span> element.
| Name | Description | Required | Default |
|---|---|---|---|
attributes | arrayAdditional attributes that should be rendered on the component where ui:attributes is used. | No | [] |
asChild | booleanIf true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. | No | - |
class | stringThe CSS class(es) to be applied to the component. | No | - |
form.successText
Permalink to heading "form.successText"Displays the current form-level success text set through the Form API, or its slotted fallback text. Renders a <span> element.
| Name | Description | Required | Default |
|---|---|---|---|
attributes | arrayAdditional attributes that should be rendered on the component where ui:attributes is used. | No | [] |
asChild | booleanIf true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. | No | - |
class | stringThe CSS class(es) to be applied to the component. | No | - |
Machine options
Permalink to heading "Machine options"| Name | Description | Default |
|---|---|---|
id | stringThe unique identifier of the form. | - |
validation | FormValidation | undefinedValidates the values of the form on the client: a Standard Schema validator such as Zod, or a synchronous callback that gets the values and returns the errors by field name. It cannot be set from a Fluid template, only by constructing Form in your own entry file. | - |
objectName | string | undefinedThe object name that prefixes the names of nested form fields. | - |
inputDebounceMs | number | undefinedHow long the fields wait after the last change before they read their value, never less than 50. A validate that sends a request should not fire on every keystroke. | 100 |
onSubmit | (({ values, api, event, post }: { values: FormValues; api: FormApi; event: JSX.FormEvent<HTMLElement>; post: (url: string) => Promise<Response>; }) => Promise<FormSubmitResult> | FormSubmitResult) | undefinedCalled with the values once the form is valid and submitted. Return true for success, false for an error, or field errors to mark the form invalid. post(url) submits the form as FormData and maps a 422 JSON response to field errors for you. | - |
render | ((form: Form) => void) | undefinedCalled every time the form state changes. Use it to update UI the form does not wire itself. | - |
Machine JavaScript API
Permalink to heading "Machine JavaScript API"| Name | Type | Description |
|---|---|---|
isSubmitting | boolean | Whether the form is being submitted. |
isDirty | boolean | Whether the value of any field differs from the one it started with. |
isInvalid | boolean | Whether any field is invalid. |
isSuccessful | boolean | Whether the last submit succeeded. |
isError | boolean | Whether the last submit ended in an error. |
getValues | () => FormValues | The current values of the form. |
getErrors | () => FormErrors | The errors of the invalid fields, by field name. |
getDirty | () => FormDirty | The dirty fields, by field name. |
getTouched | () => FormTouched | The touched fields, by field name. |
getErrorText | () => string | null | The text of the error status of the form, null if there is none. |
setErrorText | (text: string | null) => void | Sets the text of the error status. Pass null to remove it. |
getSuccessText | () => string | null | The text of the success status of the form, null if there is none. |
setSuccessText | (text: string | null) => void | Sets the text of the success status. Pass null to remove it. |
clearStatusText | () => void | Removes the error and the success text. |
getAllFields | () => Map<string, FieldHandle> | Every field of the form, by name. |
getField | (name: string) => FieldHandle | undefined | The field with this name, undefined if the form has none. |
getFormEl | () => HTMLFormElement | null | The <form> element. |
getAction | () => string | The action of the form, an empty string if it has none. |
reset | () => void | Resets the form to its initial state. |
syncFields | () => void | Picks up fields that were added or removed after the form was hydrated. |
renameField | (oldName: string, newName: string) => void | Renames a field, keeping its value, touched, dirty and error state. Useful to keep the rows of a FieldArray contiguously indexed after one in the middle was removed. |