Custom inputs (user)


There are three columns config types that do similar things, but still have subtle differences between them. These are the none type, the passthrough type and the user type.

Characteristics of user:

  • A value sent to the DataHandler is just kept as is and put into the database field. Default FormEngine however never sends values.

  • Unlike none, type user must have a database field.

  • FormEngine only renders a dummy element for type user fields by default. It should be combined with a custom renderType.

  • Type user field values are rendered as-is at other places in the backend. They for instance can be selected to be displayed in the list module "single table view".

  • Field updates by the DataHandler get logged and the history/undo function will work with such values.

The user field can be useful, if:

  • A special rendering and evaluation is needed for a value when editing records via FormEngine.


In previous versions of TYPO3 core, type='user' had a property userFunc to call an own class method of some extension. This has been substituted with a custom element using a renderType. See example below.


This example is part of the TYPO3 Documentation Team extension EXT:examples/.

The example registers an own node element, a TCA field using it and a class implementing a rendering. See FormEngine docs for more details on this.

  1. Register the new renderType node element

    Add to ext_localconf.php:

    $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][<current timestamp>] = [
        'nodeName' => 'specialField',
        'priority' => 40,
        'class' => \MyVendor\MyExtension\Form\Element\SpecialFieldElement::class,
  2. Use the renderType in a TCA field definition

    Add the field to the TCA definition, here in Configuration/TCA/Overrides/fe_users.php:

    defined('TYPO3') or die();
    $tempColumns = [
        'tx_myextension_special' => [
            'label' => 'My label',
            'config' => [
                'type' => 'user',
                // renderType needs to be registered in ext_localconf.php
                'renderType' => 'specialField',
                'parameters' => [
                    'size' => '30',
                    'color' => '#F49700',
  3. Implement the FormElement class

    The renderType can be implemented by extending the class AbstractFormElement and overriding the function render():

    namespace MyVendor\MyExtension\Form\Element;
    use TYPO3\CMS\Backend\Form\Element\AbstractFormElement;
    use TYPO3\CMS\Core\Utility\GeneralUtility;
    use TYPO3\CMS\Core\Utility\StringUtility;
    class SpecialFieldElement extends AbstractFormElement
        public function render(): array
            $row = $this->data['databaseRow'];
            $parameterArray = $this->data['parameterArray'];
            $color = $parameterArray['fieldConf']['config']['parameters']['color'];
            $size = $parameterArray['fieldConf']['config']['parameters']['size'];
            $fieldInformationResult = $this->renderFieldInformation();
            $fieldInformationHtml = $fieldInformationResult['html'];
            $resultArray = $this->mergeChildReturnIntoExistingResult($this->initializeResultArray(), $fieldInformationResult, false);
            $fieldId = StringUtility::getUniqueId('formengine-textarea-');
            $attributes = [
                'id' => $fieldId,
                'name' => htmlspecialchars($parameterArray['itemFormElName']),
                'size' => $size,
                'data-formengine-input-name' => htmlspecialchars($parameterArray['itemFormElName']),
            $attributes['placeholder'] = 'Enter special value for user "' . htmlspecialchars(trim($row['username'])) .
                '" in size ' . $size;
            $classes = [
            $itemValue = $parameterArray['itemFormElValue'];
            $attributes['class'] = implode(' ', $classes);
            $html = [];
            $html[] = '<div class="formengine-field-item t3js-formengine-field-item" style="padding: 5px; background-color: ' . $color . ';">';
            $html[] = $fieldInformationHtml;
            $html[] =   '<div class="form-wizards-wrap">';
            $html[] =      '<div class="form-wizards-element">';
            $html[] =         '<div class="form-control-wrap">';
            $html[] =            '<input type="text" value="' . htmlspecialchars($itemValue, ENT_QUOTES) . '" ';
            $html[] =               GeneralUtility::implodeAttributes($attributes, true);
            $html[] =            ' />';
            $html[] =         '</div>';
            $html[] =      '</div>';
            $html[] =   '</div>';
            $html[] = '</div>';
            $resultArray['html'] = implode(LF, $html);
            return $resultArray;


    The returned data in $resultArray['html'] will be output in the TYPO3 Backend as it is passed. Therefore don't trust user input in order to prevent cross-site scripting (XSS).

    The array $this->data consists of the following parts:

    • The row of the currently edited record in $this->data['databaseRow']

    • The configuration from the TCA in $this->data['parameterArray']['fieldConf']['config']

    • The name of the input field in $this->data['parameterArray']['itemFormElName']

    • The current value of the field in $this->data['parameterArray']['itemFormElValue']

    In order for the field to work, it is vital, that the corresponding HTML input field has a unique id attribute, fills the attributes name and data-formengine-input-name with the correct name, as provided in the itemFormElName.


    The returned data in $resultArray['html'] must be valid HTML. Invalid HTML (e.g. not closed elements) may result in unexpected behaviour in TYPO3 (e.g. new inline elements not saved).

The field would then look like this in the backend:

New fields for fe\_users table

New fields for fe_users table

This example is also described in TYPO3 Explained, Extending TCA example.



New in version 12.2: The dbType json was introduced to allow storage and usage of JSON data.

dbType (type => user)







When the property dbType is set to json, the form engine provides the decoded JSON to the RecordProviders and the user PHP implementation (extending TYPO3\CMS\Backend\Form\Element\AbstractFormElement) can then use the field value.


In the frontend and in custom backend implementations the implementing code still must decode the string into a PHP array:

If Connection->insert() or Connection->update() are used, and not specified types handed over, the database scheme is used and the native format for JSON fields data will then encoded.

This does not work for QueryBuilder. Same goes for Extbase, which is not decoding this field type yet but retrieving it as a string.

dbType = json example

The system extension EXT:reactions uses a TCA field of type user with the dbType json:


        // ...
        'fields' => [
            'label' => 'LLL:EXT:reactions/Resources/Private/Language/locallang_db.xlf:sys_reaction.fields',
            'config' => [
                'type' => 'user',
                'renderType' => 'fieldMap',
                'dbType' => 'json',
                'default' => '{}',

The implementing class receives the parsed JSON as PHP array:



namespace TYPO3\CMS\Reactions\Form\Element;

use TYPO3\CMS\Backend\Form\Element\AbstractFormElement;

class FieldMapElement extends AbstractFormElement
    public function render(): array
        $parameterArray = $this->data['parameterArray'];
        $itemValue = $parameterArray['itemFormElValue'];
        // $itemValue contains the JSON data of the field as PHP array
        // ...
        return ['Some HTML representation'];


renderType (type => user)






The default renderType simply displays a dummy entry, indicating that a custom renderType should be added. Additional render types can be defined based on the requirements of the user type field. These render types determine how the field is displayed and interacted with in the TYPO3 backend, allowing for specialized rendering and user interaction. Custom render types provide a tailored experience for editing records via the FormEngine.