Skip to main content

A powerful form component with client-side validation, AJAX submission, and seamless Extbase integration.

Use `https://down.example.com` to preview the generic error state.

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>
typo3 ui:add form

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.

The following tables cover the available props of the Fluid Primitives.

Submits and manages the form state. Renders a <form> element.

NameDescriptionRequiredDefault
actionUristring
The resolved form action URI to submit to directly.
No
-
actionstring
The Extbase action name used to build the form action URI.
No
-
extensionNamestring
The Extbase extension name used to build the form action URI.
No
-
pluginNamestring
The Extbase plugin name used to build the form action URI.
No
-
controllerstring
The Extbase controller name used to build the form action URI.
No
-
argumentsarray
The Extbase arguments used to build the form action URI.
No
-
pageUidint
The target page UID used to build the form action URI.
No
-
objectNamestring
The object name prefix used for nested form field names.
No
-
objectmixed
The bound object used for form value mapping.
No
-
methodstring
The HTTP method used for form submission.
No
'post'
attributesarray
Additional attributes that should be rendered on the component where ui:attributes is used.
No
[]
asChildboolean
If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props.
No
-
rootIdstring
The root ID of the component, used for hydration and identification.
No
-
idsarray
The IDs of of the component parts for composition.
No
[]
autoMountboolean
Whether the client initializes the component on its own. Set it to false to mount it yourself with `mount()`
No
true
classstring
The CSS class(es) to be applied to the component.
No
-

Rendered data attributes

AttributeDescription
data-stateThe state of the form. One of: "invalid", "ready", "submitting", "success", "error".
data-submittingPresent while the form is being submitted.
data-invalidPresent when invalid
data-dirtyPresent when dirty
data-touchedPresent when touched

Wraps the editable form UI. It stays visible in ready, invalid, and submitting, and hides in error and success. Renders a <div> element.

NameDescriptionRequiredDefault
attributesarray
Additional attributes that should be rendered on the component where ui:attributes is used.
No
[]
asChildboolean
If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props.
No
-
classstring
The CSS class(es) to be applied to the component.
No
-

Displays content for an exact form state such as error, success, or submitting. Renders a <div> element.

NameDescriptionRequiredDefault
stateEnum\FormState
The form state to render.
Yes
-
attributesarray
Additional attributes that should be rendered on the component where ui:attributes is used.
No
[]
asChildboolean
If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props.
No
-
classstring
The CSS class(es) to be applied to the component.
No
-

Displays the current form-level error text set through the Form API, or its slotted fallback text. Renders a <span> element.

NameDescriptionRequiredDefault
attributesarray
Additional attributes that should be rendered on the component where ui:attributes is used.
No
[]
asChildboolean
If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props.
No
-
classstring
The CSS class(es) to be applied to the component.
No
-

Displays the current form-level success text set through the Form API, or its slotted fallback text. Renders a <span> element.

NameDescriptionRequiredDefault
attributesarray
Additional attributes that should be rendered on the component where ui:attributes is used.
No
[]
asChildboolean
If true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props.
No
-
classstring
The CSS class(es) to be applied to the component.
No
-
NameDescriptionDefault
idstring
The unique identifier of the form.
-
validationFormValidation | undefined
Validates 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.
-
objectNamestring | undefined
The object name that prefixes the names of nested form fields.
-
inputDebounceMsnumber | undefined
How 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) | undefined
Called 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) | undefined
Called every time the form state changes. Use it to update UI the form does not wire itself.
-
NameTypeDescription
isSubmittingbooleanWhether the form is being submitted.
isDirtybooleanWhether the value of any field differs from the one it started with.
isInvalidbooleanWhether any field is invalid.
isSuccessfulbooleanWhether the last submit succeeded.
isErrorbooleanWhether the last submit ended in an error.
getValues() => FormValuesThe current values of the form.
getErrors() => FormErrorsThe errors of the invalid fields, by field name.
getDirty() => FormDirtyThe dirty fields, by field name.
getTouched() => FormTouchedThe touched fields, by field name.
getErrorText() => string | nullThe text of the error status of the form, null if there is none.
setErrorText(text: string | null) => voidSets the text of the error status. Pass null to remove it.
getSuccessText() => string | nullThe text of the success status of the form, null if there is none.
setSuccessText(text: string | null) => voidSets the text of the success status. Pass null to remove it.
clearStatusText() => voidRemoves the error and the success text.
getAllFields() => Map<string, FieldHandle>Every field of the form, by name.
getField(name: string) => FieldHandle | undefinedThe field with this name, undefined if the form has none.
getFormEl() => HTMLFormElement | nullThe <form> element.
getAction() => stringThe action of the form, an empty string if it has none.
reset() => voidResets the form to its initial state.
syncFields() => voidPicks up fields that were added or removed after the form was hydrated.
renameField(oldName: string, newName: string) => voidRenames 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.