---
title: "Rich text editor in the frontend form"
manual: "Events2"
version: "11.2"
permalink: "https://docs.typo3.org/permalink/jweiland/events2:frontend-rich-text-editor@11.2"
source: "AdministratorManual/FrontendRichTextEditor/Index.rst"
rendered: "2026-11-05T14:45:06+00:00"
---

# Rich text editor in the frontend form {#frontend-rich-text-editor}

<!-- TODO: no Markdown rendering for "versionadded" -->

Textarea elements of the event form based on EXT:form can be edited
with a CKEditor 5.

Textarea elements of the event form based on EXT:form (site setting
`events2.form.useExtForm`) can be edited with a small CKEditor 5. Frontend
users can format the text, and formatting added by editors in the backend
is kept when the event is edited in the frontend. By default, the element
`detail-description` (column `detail_information`) gets the editor.
Further elements can be added by configuration.

The feature is disabled by default. Without it, all elements are plain
textareas as before.

**Table of contents**

-   [Requirements for the frontend rich text editor](https://docs.typo3.org/permalink/jweiland/events2:requirements-for-the-frontend-rich-text-editor@11.2)
-   [Site settings of the frontend rich text editor](https://docs.typo3.org/permalink/jweiland/events2:site-settings-of-the-frontend-rich-text-editor@11.2)
-   [Enable the frontend rich text editor](https://docs.typo3.org/permalink/jweiland/events2:enable-the-frontend-rich-text-editor@11.2)
-   [Rich text elements of the event form](https://docs.typo3.org/permalink/jweiland/events2:rich-text-elements-of-the-event-form@11.2)
-   [Loaded files of the frontend rich text editor](https://docs.typo3.org/permalink/jweiland/events2:loaded-files-of-the-frontend-rich-text-editor@11.2)
-   [Toolbar of the frontend rich text editor](https://docs.typo3.org/permalink/jweiland/events2:toolbar-of-the-frontend-rich-text-editor@11.2)
-   [Allowed markup of the frontend rich text editor](https://docs.typo3.org/permalink/jweiland/events2:allowed-markup-of-the-frontend-rich-text-editor@11.2)
-   [Modify the allowed markup of the frontend rich text editor](https://docs.typo3.org/permalink/jweiland/events2:modify-the-allowed-markup-of-the-frontend-rich-text-editor@11.2)
-   [Frontend form without JavaScript](https://docs.typo3.org/permalink/jweiland/events2:frontend-form-without-javascript@11.2)

## Requirements for the frontend rich text editor {#frontend-rich-text-editor-requirements}

-   The event form based on EXT:form (site setting
    `events2.form.useExtForm`).
-   The system extension EXT:rte_ckeditor
    ([`typo3/cms-rte-ckeditor`](https://packagist.org/packages/typo3/cms-rte-ckeditor)). The editor is not activated if
    it is not installed.

No build step (npm, webpack) is needed. The CKEditor 5 ES modules,
translations and styles shipped with EXT:rte_ckeditor are used.

## Site settings of the frontend rich text editor {#frontend-rich-text-editor-site-settings}

The following settings are provided by the site set `jweiland/events2`
and are available in TypoScript as
`plugin.tx_events2.settings.new.richText`, see
[richText.enable](https://docs.typo3.org/permalink/jweiland/events2:typoscript-new-richtext-enable@11.2).

-   **events2.form.richText.enable**

    -   *Type:* bool
    -   *Default:* `false`

    Use CKEditor 5 of EXT:rte_ckeditor for the rich text elements of
    the event form based on EXT:form.

-   **events2.form.richText.elements**

    -   *Type:* string
    -   *Default:* `detail-description`

    Comma-separated identifiers of the form elements which get the rich
    text editor. Elements with `dataType: richText` in their
    `dbMapping` get the editor, too.

## Enable the frontend rich text editor {#frontend-rich-text-editor-enable}

Enable the site setting
[events2.form.richText.enable](https://docs.typo3.org/permalink/jweiland/events2:confval-events2-form-richtext-enable@11.2)
of the site set `jweiland/events2`.

That is all for the description: the element `detail-description` is the
default of the site setting
[events2.form.richText.elements](https://docs.typo3.org/permalink/jweiland/events2:confval-events2-form-richtext-elements@11.2).
This works for overridden copies of `NewEvent.form.yaml` as well, as
long as they use this identifier.

### Example: Enable the rich text editor for the description {#frontend-rich-text-editor-example-description}

**config/sites/\<my-site>/settings.yaml**

```yaml
events2:
  form:
    richText:
      enable: true
```

## Rich text elements of the event form {#frontend-rich-text-editor-elements}

An element of the form is a rich text element if

-   its identifier is listed in the site setting
    [events2.form.richText.elements](https://docs.typo3.org/permalink/jweiland/events2:confval-events2-form-richtext-elements@11.2)
    or
-   it has `dataType: richText` in its `dbMapping`.

If the editor is enabled, events2 configures each rich text element
automatically:

-   The `dataType` of the element is `richText`, so the edit
    form gets the stored HTML instead of plain text.
-   The mapping of the element in the `SaveEventFinisher` gets
    `sanitizeRichText: true` (instead of
    `convertPlainTextToHtml` or `normalizePlainText`).
-   While rendering, the textarea gets the attribute
    `data-events2-rich-text` with the options of the editor,
    including the allowed tags of its column (see
    [Toolbar of the frontend rich text editor](https://docs.typo3.org/permalink/jweiland/events2:frontend-rich-text-editor-toolbar@11.2)). The assets are only loaded
    on pages of the form which contain a rich text element. Each textarea
    gets its own editor.

If the editor is disabled, rich text elements fall back to plain text:
`dataType: plainText` and `convertPlainTextToHtml` instead of
`sanitizeRichText`.

> [!IMPORTANT]
> The database column of a rich text element should be a rich text field
> in TCA (`'enableRichtext' => true`), like `detail_information`.
> Otherwise, the backend shows the HTML source of the value. The column
> `teaser`, for example, is a plain text field with a maximum of 255
> characters and is **not** a suitable target, unless you change its TCA.

The editor of an element is configured for the column
`properties.dbMapping.column` of the table
`properties.dbMapping.table`. The table is optional and defaults to
`tx_events2_domain_model_event`. Set it only for elements which are stored
in another table, so the toolbar matches the allowed tags of that column.
Use the same table as the mapping of the element in the
`SaveEventFinisher`:

**EXT:my_sitepackage/Configuration/Form/NewEvent.form.yaml (excerpt)**

```yaml
- label: 'Notes of the organizer'
  type: Textarea
  identifier: organizer-notes
  properties:
    dbMapping:
      table: tx_events2_domain_model_organizer
      column: tx_myext_notes
      dataType: richText
```

### Example: Add a rich text editor to an additional form element {#frontend-rich-text-editor-example-additional-element}

Add an own textarea to your copy of `NewEvent.form.yaml` and map it
to an own column of the event table, which you have configured with
`'enableRichtext' => true` in TCA:

**EXT:my_sitepackage/Configuration/Form/NewEvent.form.yaml (excerpt)**

```yaml
- label: 'Program'
  type: Textarea
  identifier: program
  properties:
    dbMapping:
      column: tx_myext_program
      # Optional: Instead of listing "program" in the site setting
      dataType: richText
```

Map the element in the `SaveEventFinisher` options of the table
`tx_events2_domain_model_event`:

**EXT:my_sitepackage/Configuration/Form/NewEvent.form.yaml (excerpt)**

```yaml
finishers:
  1:
    identifier: SaveEventFinisher
    options:
      4:
        table: tx_events2_domain_model_event
        elements:
          program:
            mapOnDatabaseColumn: tx_myext_program
```

Then either keep `dataType: richText` in the form definition or add
the identifier to the site setting:

**config/sites/\<my-site>/settings.yaml**

```yaml
events2:
  form:
    richText:
      enable: true
      elements: 'detail-description, program'
```

## Loaded files of the frontend rich text editor {#frontend-rich-text-editor-assets}

The import map of the CKEditor modules is defined in
`EXT:events2/Configuration/JavaScriptModules.php` and rendered by the
`PageRenderer` of TYPO3 (`<script type="importmap">`,
respecting the Content Security Policy of the site). The ES module
`EXT:events2/Resources/Public/JavaScript/rich-text-editor.js` creates
an editor for each textarea with the attribute
`data-events2-rich-text`. It loads the UI translation of the current
site language. The style sheets
`EXT:rte_ckeditor/Resources/Public/Css/editor.css` and
`EXT:events2/Resources/Public/Css/RichTextEditor.css` are added with
the `AssetCollector`.

The files are about 1.6 MB uncompressed and about 400 KB compressed with
gzip.

## Toolbar of the frontend rich text editor {#frontend-rich-text-editor-toolbar}

The toolbar is built from the tags which are allowed for the column of
the element (see [Allowed markup of the frontend rich text editor](https://docs.typo3.org/permalink/jweiland/events2:frontend-rich-text-editor-allowed-markup@11.2)). With
the default configuration, it contains:

-   Heading: Paragraph, Heading 2, Heading 3 (Heading 1 is used for the
    title of the event)
-   Bold, Italic
-   Bulleted list, numbered list
-   Link with the protocols `http`, `https`, `mailto` and `tel` and the
    option **Open in a new tab**
-   Undo, Redo

<!-- TODO: no Markdown rendering for "versionadded" -->

The headings and toolbar items follow the allowed tags of the
ModifyRichTextSanitizerConfigurationEvent.

The editor gets one heading option for each allowed element of
`<h1>` to `<h6>`. The titles of the headings are translated
by CKEditor. The heading dropdown is only shown if at least one heading is
allowed. The toolbar items are only shown if their element is allowed:

-   **Bold**: `<strong>`
-   **Italic**: `<em>`
-   **Bulleted list**: `<ul>`
-   **Numbered list**: `<ol>`
-   **Link**: `<a>`

**Undo** and **Redo** are always available. Allowed
elements without a toolbar item, like `<u>` or
`<blockquote>`, are kept when the value is saved, but can not be
selected in the editor.

Toolbar items which do not fit into the width of the form are grouped into
a dropdown. The toolbar can be focused with `Alt` \+ `F10`.

## Allowed markup of the frontend rich text editor {#frontend-rich-text-editor-allowed-markup}

The submitted HTML is sanitized on the server with
[`typo3/html-sanitizer`](https://packagist.org/packages/typo3/html-sanitizer), as a request can bypass the editor. By
default, only these tags are allowed:

-   `<p>`, `<br>`, `<strong>`, `<em>`,
    `<h2>`, `<h3>`, `<ul>`, `<ol>`, `<li>`
-   `<a>` with `href` (`http`, `https`, `mailto`, `tel`) and
    `target="_blank"`, which gets `rel="noopener noreferrer"`

`<b>` is converted to `<strong>`, `<i>` to `<em>`
and `<h1>`, `<h4>`, `<h5>`, `<h6>` to
`<p>`. All attributes like `class`, `style` or
`on*` are removed. Links with other protocols like `javascript:` or
`data:` are removed, their text is kept. `<script>`,
`<style>`, `<iframe>`, images, forms and similar elements are
removed including their content. The text of all other elements is kept.
Other block elements like `<div>`, `<blockquote>` or table
cells become paragraphs. Text and inline formatting outside of blocks is
wrapped into a paragraph like the editor does, so `<strong>`,
`<em>` and links are kept.

The tags can be modified with the PSR-14 event
`ModifyRichTextSanitizerConfigurationEvent`, see
[Modify the allowed markup of the frontend rich text editor](https://docs.typo3.org/permalink/jweiland/events2:frontend-rich-text-editor-sanitizer-event@11.2).

### Links to records (t3://) in the frontend rich text editor {#frontend-rich-text-editor-t3-links}

Links like `t3://page?uid=12` or `t3://file?uid=1234` can only be added in
the backend. They are kept if exactly the same link is already part of the
value stored in the column of the record which is updated. New or modified
`t3://` links are removed, their text is kept. For records of the event
table, the event of the edit form is used. For other tables, the `uid` of
the `whereClause` of the finisher (mode `update`) is used.

Afterwards, the value is transformed with the RTE configuration of the
backend (`RteHtmlParser`), so it is stored the same way as if it was
saved in the backend.

## Modify the allowed markup of the frontend rich text editor {#frontend-rich-text-editor-sanitizer-event}

<!-- TODO: no Markdown rendering for "versionadded" -->

The PSR-14 event ModifyRichTextSanitizerConfigurationEvent
allows to modify the tags of the rich text sanitizer and of the
toolbar of the frontend editor.

The PSR-14 event
`JWeilandEvents2EventModifyRichTextSanitizerConfigurationEvent`
is dispatched for each rich text element:

-   While rendering the form element, to build the headings and toolbar
    items of the editor (see [Toolbar of the frontend rich text editor](https://docs.typo3.org/permalink/jweiland/events2:frontend-rich-text-editor-toolbar@11.2)).
    Table and column are taken from `properties.dbMapping.table`
    (default: `tx_events2_domain_model_event`) and
    `properties.dbMapping.column` of the form element.
-   Each time a submitted value which contains HTML is sanitized. Values
    without any tag are converted like plain text and do not dispatch the
    event.

The event receives the defaults, which are defined as constants in
`JWeilandEvents2SanitizerVisitorRichTextVisitor`. So one
listener configures both the editor and the sanitizing on the server.

> [!NOTE]
> Only elements with a toolbar item can be selected in the editor:
> headings `<h1>` to `<h6>`, `<strong>`, `<em>`,
> `<ul>`, `<ol>` and `<a>`. Other allowed elements, like
> `<u>` or `<blockquote>`, are kept if they are already part
> of the value, for example because they were added by an editor in the
> backend, or if they are submitted with a request.

### Methods of the ModifyRichTextSanitizerConfigurationEvent {#frontend-rich-text-editor-sanitizer-event-methods}

-   **`getAllowedTags()` / `setAllowedTags(array $allowedTags)`**

    Elements which are kept. Default: `<p>`, `<br>`,
    `<strong>`, `<em>`, `<h2>`, `<h3>`,
    `<ul>`, `<ol>`, `<li>` and `<a>`.

-   **`getMappedTags()` / `setMappedTags(array $mappedTags)`**

    Elements which are renamed, as array of source tag name to target tag
    name, for example `'b' => 'strong'`. Default: `<b>` to
    `<strong>`, `<i>` to `<em>` and `<h1>`,
    `<h4>`, `<h5>`, `<h6>` to `<p>`.

-   **`getRemovedTags()` / `setRemovedTags(array $removedTags)`**

    Elements which are removed including their content, for example
    `<script>`, `<img>`, `<form>` or `<video>`.

-   **`getBlockTags()` / `setBlockTags(array $blockTags)`**

    Elements which start a new block, for example `<div>`,
    `<blockquote>` or `<td>`. A block element which is not
    allowed becomes a paragraph if it does not contain other block
    elements. Otherwise, it is unwrapped and only its content is kept.

-   **`getTable()`**

    Table of the column the HTML is stored in, for example
    `tx_events2_domain_model_event`. Empty if unknown.

-   **`getColumn()`**

    Column the HTML is stored in, for example `detail_information`. Empty
    if unknown.

### Rules for the configuration of the rich text sanitizer {#frontend-rich-text-editor-sanitizer-event-rules}

After all listeners are called, the configuration is normalized:

-   Tag names are converted to lower case. Invalid tag names are ignored.
-   `<p>` is always allowed, as inline content outside of blocks is
    wrapped into paragraphs.
-   Elements with active or embedded content can never be allowed:
    `<applet>`, `<base>`, `<embed>`, `<frame>`,
    `<frameset>`, `<iframe>`, `<link>`, `<math>`,
    `<meta>`, `<noscript>`, `<object>`,
    `<script>`, `<style>`, `<svg>` and
    `<template>`. They are always removed including their content.
-   Removed tags take precedence over allowed tags.
-   A mapping is only used if its target is allowed and its source is
    neither allowed nor removed. So allowing `<h4>` is enough to keep
    it, the default mapping of `<h4>` to `<p>` is skipped.
-   Allowed elements never get any attribute. The only exception is
    `<a>`, which keeps a checked `href` and
    `target="_blank"`.

After sanitizing, the value is transformed with the RTE configuration of
the backend (`RteHtmlParser`). An element allowed by the event must
be allowed by the RTE configuration of the column as well. The default RTE
configuration keeps `<h4>`, for example.

### Example: Allow h4 in the description of events {#frontend-rich-text-editor-sanitizer-event-example}

The following event listener allows `<h4>` for the column
`detail_information` of the event table only. The editor of the
description gets the additional heading **Heading 4**, and the
heading is kept when the event is saved. All other rich text elements
keep the default configuration.

**EXT:my_sitepackage/Classes/EventListener/AllowHeading4EventListener.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MySitepackage\EventListener;

use JWeiland\Events2\Event\ModifyRichTextSanitizerConfigurationEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;

#[AsEventListener(identifier: 'my-sitepackage/allow-heading-4')]
final class AllowHeading4EventListener
{
    public function __invoke(ModifyRichTextSanitizerConfigurationEvent $event): void
    {
        if ($event->getTable() !== 'tx_events2_domain_model_event'
            || $event->getColumn() !== 'detail_information'
        ) {
            return;
        }

        // Keep <h4> instead of converting it to <p>
        $event->setAllowedTags([...$event->getAllowedTags(), 'h4']);
    }
}

```

The attribute `#[AsEventListener]` registers the listener, see
[Event dispatcher (PSR-14 events)](https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/Events/EventDispatcher/Index.html#eventdispatcher).

## Frontend form without JavaScript {#frontend-rich-text-editor-no-javascript}

Without JavaScript, the plain textarea is shown. For new events, plain text
can be entered as before. When editing an event, the textarea contains the
stored HTML. Values without any tag are converted like the plain text
description.
