---
title: "Breaking: #97330 - FormEngine element classes must create label or legend"
manual: "TYPO3 Core Changelog"
version: "main"
permalink: "https://docs.typo3.org/permalink/changelog:breaking-97330-1687870738"
source: "Changelog/13.0/Breaking-97330-FormEngineElementClassesMustCreateLabelOrLegend.rst"
modified: "2026-09-15T10:46:36+00:00"
---

# Breaking: #97330 - FormEngine element classes must create label or legend

See [forge#97330](https://forge.typo3.org/issues/97330)

## Description

When editing records in the backend, the `FormEngine` class structure located
within `EXT:backend/Classes/Form/` handles the generation of the editing view.

A change has been applied related to the rendering of single field labels, which
is no longer done automatically by "container" classes: Single elements have to
create the label themselves.

Extension that add own elements to FormEngine must be adapted, otherwise the
element label is no longer rendered.

## Impact

When the required changes are not applied to custom FormEngine element classes,
the value of the TCA "label" property is not rendered.

## Affected installations

Instances with custom FormEngine elements are affected. Custom elements need to be
registered to the FormEngine's `NodeFactory`, candidates are found by looking at
the `$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']` array (for instance
using the **System > Configuration** backend module provided by
EXT:lowlevel). Classes registered using the sub keys `nodeRegistry` and
`nodeResolver` may be affected. The extension scanner does not find
affected classes.

## Migration

Custom elements must take care of creating a `<label>` or `<legend>`
tag on their own: If the element creates an `<input>`, or `<select>` tag,
the `<label>` should have a `for` attribute that points to a field having
an `id` attribute. This is important especially for accessibility. When no such
target element exists, a `<legend>` embedded in a `<fieldset>` can be used.
There are two helper methods in
`\TYPO3\CMS\Backend\Form\Element\AbstractFormElement` to help with this:
`renderLabel()` and `wrapWithFieldsetAndLegend()`.

In practice, an element having an `<input>`, or `<select>` field should
essentially look like this:

```php
$resultArray = $this->initializeResultArray();
// Next line is only needed for extensions that need to keep TYPO3 v12 compatibility
$resultArray['labelHasBeenHandled'] = true;
$fieldId = StringUtility::getUniqueId('formengine-input-');
$html = [];
$html[] = $this->renderLabel($fieldId);
$html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
$html[] =     '<div class="form-wizards-wrap">';
$html[] =         '<div class="form-wizards-element">';
$html[] =             '<div class="form-control-wrap">';
$html[] =                 '<input class="form-control" id="' . htmlspecialchars($fieldId) . '" value="..." type="text">';
$html[] =             '</div>';
$html[] =         '</div>';
$html[] =     '</div>';
$html[] = '</div>';
$resultArray['html'] = implode(LF, $html);
return $resultArray;
```

The `renderLabel()` is a helper method to generate a `<label>` tag with a
`for` attribute, and the same fieldId is used as `id` attribute in the
`<input>` field to connect `<label>` and `<input>` with each other.

If there is no such field, a `<legend>` tag should be used:

```php
$resultArray = $this->initializeResultArray();
// Next line is only needed for extensions that need to keep TYPO3 v12 compatibility
$resultArray['labelHasBeenHandled'] = true;
$html = [];
$html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
$html[] =     '<div class="form-wizards-wrap">';
$html[] =         '<div class="form-wizards-element">';
$html[] =             '<div class="form-control-wrap">';
$html[] =                 Some custom element html
$html[] =             '</div>';
$html[] =         '</div>';
$html[] =     '</div>';
$html[] = '</div>';
$resultArray['html'] = $this->wrapWithFieldsetAndLegend(implode(LF, $html));
return $resultArray;
```
