Plugging in a custom RTE

TYPO3 supports any Rich Text Editor for which someone might write a connector to the RTE API. This means that you can freely choose whatever RTE you want to use among those available from the Extension Repository on

TYPO3 comes with a built-in RTE called "ckeditor", but other RTEs are available in the TYPO3 Extension Repository and you can implement your own RTE if you like.

API for rich text editors

Connecting an RTE in an extension to TYPO3 is easy. The following example is based on the implementation of ext:rte_ckeditor.

  • In the ext_localconf.php you can use the FormEngine's NodeResolver to implement your own RichTextNodeResolver and give it a higher priority than the Core's implementation:

    use MyVendor\MyExtension\Form\Resolver\RichTextNodeResolver;
    defined('TYPO3') or die();
    $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeResolver'][1593194137] = [
        'nodeName' => 'text',
        'priority' => 50, // rte_ckeditor uses priority 50
        'class' => RichTextNodeResolver::class,
  • Now create the class \MyVendor\MyExtension\Form\Resolver\RichTextNodeResolver. The RichTextNodeResolver needs to implement the NodeResolverInterface and the major parts happen in the resolve() function, where, if all conditions are met, the RichTextElement class name is returned:

    namespace MyVendor\MyExtension\Form\Resolver;
    use MyVendor\MyExtension\Form\Element\RichTextElement;
    use TYPO3\CMS\Backend\Form\NodeFactory;
    use TYPO3\CMS\Backend\Form\NodeResolverInterface;
    use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
     * This resolver will return the RichTextElement render class if RTE is enabled for this field.
    class RichTextNodeResolver implements NodeResolverInterface
         * Global options from NodeFactory
        protected array $data;
         * Default constructor receives full data array
        public function __construct(NodeFactory $nodeFactory, array $data)
            $this->data = $data;
         * Returns RichTextElement as class name if RTE widget should be rendered.
         * @return string|null New class name or void if this resolver does not change current class name.
        public function resolve(): string|null
            $parameterArray = $this->data['parameterArray'];
            $backendUser = $this->getBackendUserAuthentication();
            if (// This field is not read only
                // If RTE is generally enabled by user settings and RTE object registry can return something valid
                && $backendUser->isRTE()
                // If RTE is enabled for field
                && isset($parameterArray['fieldConf']['config']['enableRichtext'])
                && (bool)$parameterArray['fieldConf']['config']['enableRichtext'] === true
                // If RTE config is found (prepared by TcaText data provider)
                && isset($parameterArray['fieldConf']['config']['richtextConfiguration'])
                && is_array($parameterArray['fieldConf']['config']['richtextConfiguration'])
                // If RTE is not disabled on configuration level
                && !$parameterArray['fieldConf']['config']['richtextConfiguration']['disabled']
            ) {
                return RichTextElement::class;
            return null;
        protected function getBackendUserAuthentication(): BackendUserAuthentication
            return $GLOBALS['BE_USER'];
  • Next step is to implement the RichtTextElement class. You can look up the code of EXT:rte_ckeditor/Classes/Form/Element/RichTextElement.php, which does the same for ckeditor. What basically happens in its render() function, is to apply any settings from the fields TCA config and then printing out all of the html markup and javascript necessary for booting up the ckeditor.