TYPO3 Content Blocks

Extension key

content_blocks

Package name

friendsoftypo3/content-blocks

Version

main

Language

en

Author

TYPO3 Content Types Team & TYPO3 contributors

License

This document is published under the Creative Commons BY-NC-SA 4.0 license.

Rendered

Mon, 23 Jun 2025 10:45:03 +0000

The TYPO3 Content Blocks extension, friendsoftypo3/content-blocks , provides a simplified way to register and use content types as sub-resources inside extensions.

Introduction

What is a Content Block?

Installation

Install the Content Blocks extension.

Definition

Learn about which parts a Content Block consists.

Kickstart command

Kickstart a new Content Block.

YAML reference

Reference of all available settings.

Templating

Learn to use Content Blocks inside your Fluid template.

API

Deep dive into the Content Blocks API.

Guides

Comprehensive guides for specific use cases.

Developer corner

Features targeted to developers specifically.

Changelog

Documentation of all changes.

Table of Contents

Introduction

A Content Block is a simplified, component-based approach of defining Content Types in TYPO3. This includes Content Elements, Page Types and generic Record Types. A YAML file serves as a central definition. Content Blocks acts hereby as a compiler for more complex low-level code. It adheres to best practices by default, significantly reducing boilerplate code.

For more technical, historical and conceptual insights about Content Blocks we recommend these further readings:

Quick start

If you use the Site Package Builder with the "Site Package Tutorial" package the generated site package contains two example Content Blocks. They are also explained in the Site Package Tutorial, chapter Custom Content Blocks.

Definition

The goal is to encapsulate all resources belonging to the Content Block inside one component. This leads to re-usable components, which can be easily copy-pasted into other projects.

  • my-content-block

    • assets

      • icon.svg
    • language

      • labels.xlf
    • templates

      • backend-preview.html
      • frontend.html
    • config.yaml

config.yaml

This file is the basis for the definition. It defines exactly one Content Type. Using YAML over PHP includes a wider range of people, which is able to modify Content Blocks without the need of a developer.

EXT:some_extension/ContentBlocks/ContentElements/content-block-name/config.yaml
name: vendor/content-block-name
fields:
  - identifier: my_text_field
    type: Text
Copied!

Registration

The registration works by simply placing a Content Block into a dedicated folder. For this purpose an already loaded extension is required as a host. Depending on the Content Type the Content Block is put into a predestinated sub-folder.

  • my_extension

    • Classes
    • Configuration
    • ContentBlocks

      • ContentElements

        • content-block-1
        • content-block-2
      • PageTypes

        • content-block-3
        • content-block-4
      • RecordTypes

        • content-block-5
        • content-block-6
    • ext_emconf.php
    • composer.json

Terminology

Content Blocks is the name of the extension and generates the code for the Core API.

A single Content Block is a small chunk of information, which defines exactly one Content Type.

A Content Type is an entity in TYPO3, which defines a set of fields and their behavior.

A Content Element is a special Content Type, which has a frontend rendering definition.

A Page Type is a special Content Type, which defines the behavior of a web page.

A Record Type is a generic Content Type.

ContentBlockContentTypeContentElementPageTypeRecordType

Installation

Composer installations

composer require friendsoftypo3/content-blocks
Copied!

Classic mode

Activate the Content Blocks extension in the Extension Manager.

Security

In classic mode it is important to deny access to the Content Blocks folder by the webserver. For this a small adjustment to the standard TYPO3 .htaccess file in the section "Access block for folders" is needed:

.htaccess.diff
-RewriteRule (?:typo3conf/ext|typo3/sysext|typo3/ext)/[^/]+/(?:Configuration|Resources/Private|Tests?|Documentation|docs?)/ - [F]
+RewriteRule (?:typo3conf/ext|typo3/sysext|typo3/ext)/[^/]+/(?:Configuration|ContentBlocks|Resources/Private|Tests?|Documentation|docs?)/ - [F]
Copied!
nginx configuration
-location ~ (?:typo3conf/ext|typo3/sysext|typo3/ext)/[^/]+/(?:Configuration|Resources/Private|Tests?|Documentation|docs?)/ {
+location ~ (?:typo3conf/ext|typo3/sysext|typo3/ext)/[^/]+/(?:Configuration|ContentBlocks|Resources/Private|Tests?|Documentation|docs?)/ {
     deny all;
 }
Copied!

Definition

The minimal viable definition consists of a folder with a YAML file named config.yaml inside. All other resources are split into three folders named assets, templates and language.

  • my-content-block

config.yaml

The heart of a Content Block is the config.yaml file. This YAML file defines both the available fields and the structure:

EXT:some_extension/ContentBlocks/ContentElements/content-block-name/config.yaml
name: vendor/content-block-name
fields:
  - identifier: header
    useExistingField: true
  - identifier: my_text_field
    type: Text
    max: 10
Copied!

First of all, a name has to be defined. It must be unique inside your installation. It consists, similarly to composer package names, of a vendor and a package part separated by a slash. It is used to prefix new field names, new tables and record type identifiers.

Inside fields you define the structure and configuration of the necessary fields. The identifier has to be unique per Content Block.

It is possible to reuse existing fields with the flag useExistingField. This allows e.g. to use the same field header or bodytext across multiple Content Blocks with different configuration. Be aware that system fields shouldn't be reused. A list of sane reusable fields can be referenced in the documentation. Furthermore, own custom fields can be reused as well.

assets

The assets folder contains public resources. If you are familiar with the directory structure of extensions, this would be the Resources/Public folder. This is the place where you can put your CSS, JavaScript or image files inside. In order to include these in your template, you must use custom Content Block ViewHelpers.

This folder will be symlinked or copied to Resources/Public/ContentBlocks/<vendor>/<name> of the host extension. This is to keep the modular style of a Content Type but still leverage the TYPO3 asset publishing mechanism.

icon.svg

This is the icon for the Content Type. There is a fallback to a default icon, but it is recommended to replace it with your own, custom icon. You can find many official TYPO3 icons here. Allowed file extensions are svg, png and gif (in preferred order).

icon-hide-in-menu.svg

This is a special icon for Page Types for the "hide in menu" state. The same logic applies as for the standard icon.svg.

icon-root.svg

This is a special icon for Page Types for the "is_siteroot" state. The same logic applies as for the standard icon.svg.

templates

The templates folder contains private resources. If you are familiar with the directory structure of extensions, this would be the Resources/Private folder. There is a limited set of directories and files, which you can place here.

backend-preview.html

The backend-preview.html can be added to customize the backend preview for your editors.

Learn more about backend previews.

frontend.html

This is the default frontend rendering definition for Content Elements. You can access your fields by the variable {data}.

Learn more about templating.

partials

For larger Content Elements, you can divide your frontend.html template into smaller chunks by creating separate partials here.

Partials are included as you normally would in any Fluid template.

<f:render partial="Component" arguments="{_all}"/>
Copied!

See also:

layouts

You can also add layouts to your Content Block if needed.

<f:layout name="MyLayout">
Copied!

language

This is the folder for your translations. In fact, if you only have one language, there is no actual need to maintain translations here. However, it is best practice to separate labels and configuration.

labels.xlf

This XLF file is the english basis for your translations. All translations for backend labels as well as for frontend labels are defined here. Translations to other languages are defined in separate files prefixed with the language code e.g. de.labels.xlf.

Registration

In order to register a new Content Block, a folder ContentBlocks has to be created on the root level inside a loaded extension. Depending on the Content Type you want to create, you place the new Content Block into a dedicated folder. These are named ContentElements, PageTypes and RecordTypes.

  • my_extension

    • Classes
    • Configuration
    • ContentBlocks

      • ContentElements

        • content-block-1
        • content-block-2
      • PageTypes

        • content-block-3
        • content-block-4
      • RecordTypes

        • content-block-5
        • content-block-6
    • ext_emconf.php
    • composer.json

The system loads them automatically as soon as it finds any folder inside these directories, which has a file with the name config.yaml inside. Refer to the YAML reference, on how to define this file.

Administration

Make sure to add Content Blocks as a dependency in the host extension.

You will need to allow the generated database fields, tables (if using Collections) and CType in the backend user group permissions.

Kickstart command

The command make:content-block creates a bare-minimum Content Block. This is actually an alias for content-blocks:create, which is inspired by the EXT:make extension.

Options

Name Type Default Required
string content-element true
string (vendor of root composer.json) true
string true
string true
string
string|int
string content-blocks-skeleton
string content-blocks.yaml

content-type

content-type
Type
string
Required

true

Default
content-element

content-element, page-type or record-type

vendor

vendor
Type
string
Required

true

Default
(vendor of root composer.json)

Your vendor name. Lowercase, separated by dashes.

name

name
Type
string
Required

true

Your Content Block name (this is not the title). Lowercase, separated by dashes.

extension

extension
Type
string
Required

true

The host extension, where to store your new Content Block.

title

title
Type
string

The human-readable title for your Content Block.

type-name

type-name
Type
string|int

Custom type name. Required for content-type page-type (must be int).

skeleton-path

skeleton-path
Type
string
Default
content-blocks-skeleton

A path relative to the current working directory, which holds a skeleton of a Content Block. Only needed, if you want to use a different name or path other than content-blocks-skeleton.

  • content-blocks-skeleton

    • content-element

      • assets

        • icon.svg
      • templates

        • backend-preview.html
        • frontend.html
    • page-type
    • record-type

Learn more about Content Blocks skeleton

config-path

config-path
Type
string
Default
content-blocks.yaml

A path to a yaml config file path, which contains defaults for this command.

Learn more about Content Blocks defaults

This will give you an overview of all available options:

vendor/bin/typo3 make:content-block --help
Copied!

Example creating a Content Block skeleton in one line:

vendor/bin/typo3 make:content-block --content-type="content-element" --vendor="my-vendor" --name="my-name" --title="My shiny new Content Element" --extension="my_sitepackage"
Copied!

Alternatively, the command can guide you through the creation by omitting the required options:

vendor/bin/typo3 make:content-block
Copied!

On non-composer installations use:

typo3/sysext/core/bin/typo3 make:content-block
Copied!

Example interaction:

Choose the content type of your content block [Content Element]:
[content-element] Content Element
[page-type      ] Page Type
[record-type    ] Record Type
> content-element

Enter your vendor name:
> my-vendor

Enter your content block name:
> my-content-block-name

Choose an extension in which the content block should be stored:
[sitepackage] Test Package for content blocks
> sitepackage
Copied!

After running the make command

In order to create newly added database tables or fields, you have to clear the caches and then run the database compare. You can do the same in the TYPO3 Backend by using the Database Analyzer. Repeat this step every time you add new fields to your Content Block definition.

vendor/bin/typo3 cache:flush -g system
vendor/bin/typo3 extension:setup --extension=my_sitepackage
Copied!

Content Block skeleton

New in version 1.1

It is now possible to define a "skeleton" for your Content Blocks. To do this create a folder called content-blocks-skeleton in your project root. This folder may contain default templates or assets for one or more Content Types. It is used as a base when creating new types with the make:content-block command. In order to add a skeleton for Content Elements, create a folder called content-element within that directory. Then, the structure is identical to your concrete Content Block as you know it. You may place any files there. They will simply be copied when a new Content Block is created. It is not possible to define language/labels.xlf or config.yaml this way, as they are dynamically generated based on your arguments.

  • content-blocks-skeleton

    • content-element

      • assets

        • icon.svg
      • templates

        • backend-preview.html
        • frontend.html
    • page-type
    • record-type

In case you want to name the skeleton folder differently or place it somewhere else, you can override the default folder by providing the option --skeleton-path with a relative path to your current working directory.

You can use an alternative skeleton path
vendor/bin/typo3 make:content-block --skeleton-path="my-alternative-skeleton-path"
Copied!

Defaults

New in version 1.1

It is now possible to define default options for this command via a yaml config file. By default, the command looks for a file called content-blocks.yaml in the current working directory. The location and name can be overridden with the --config-path option.

vendor/bin/typo3 make:content-block --config-path="some-folder/my-config.yaml"
Copied!

An example yaml config file contents may look like this:

content-blocks.yaml
vendor: nh
extension: content_blocks_examples
content-type: record-type
skeleton-path: folder1/content-block-skeletons
Copied!

This config sets defaults for vendor, skeleton-path, extension and content-type. These are all possible options right now.

Now, whenever you run this command, these options will be set by default. This does not mean, the questions for these options will be skipped, only that they are the default value, if you just press Enter without any input. They will be visible in brackets at the very right [default value].

New in version 1.3

The content-blocks.yaml file supports arbitrary default values for the generated config.yaml file.

Add a config key followed by a Content Type identifier. In this case, we set default values for Content Elements, so we use content-element. You can also set default values for page-type, record-type or file-type. Values defined in here override the generated configuration by the command.

config:
  content-element:
    basics:
      - TYPO3/Appearance
      - TYPO3/Links
    group: my_group
    prefixFields: true
    prefixType: vendor
Copied!

List command

The command content-blocks:list lists all loaded Content Blocks inside a table.

Options

order

order
Required

false

Type

string

Shortcut

o

vendor, name, table, type-name, content-type or extension

This will give you an overview of all available Content Blocks:

vendor/bin/typo3 content-blocks:list
Copied!

Alternatively, you can specify a different order in which to sort the result:

vendor/bin/typo3 content-blocks:list --order content-type
Copied!

Example output:

+-----------+-------------------+-------------------------+-----------------+-----------------------------+
| vendor    | name              | extension               | content-type    | table                       |
+-----------+-------------------+-------------------------+-----------------+-----------------------------+
| example   | tabs              | content_blocks_examples | Content Element | tt_content                  |
| example   | accordion         | content_blocks_examples | Content Element | tt_content                  |
| example   | card-group        | content_blocks_examples | Content Element | tt_content                  |
| example   | cta               | content_blocks_examples | Content Element | tt_content                  |
| example   | icon-group        | content_blocks_examples | Content Element | tt_content                  |
| example   | imageslider       | content_blocks_examples | Content Element | tt_content                  |
| example   | example-page-type | content_blocks_examples | Page Type       | pages                       |
| hov       | record1           | content_blocks_examples | Record Type     | tx_hov_domain_model_record1 |
| hov       | record2           | content_blocks_examples | Record Type     | tx_hov_domain_model_record1 |
| hov       | notype            | content_blocks_examples | Record Type     | tx_hov_domain_model_notype  |
+-----------+-------------------+-------------------------+-----------------+-----------------------------+
Copied!

Language Generate command

The command content-blocks:language:generate updates the labels.xlf content for the specified Content Block. Already set labels in both the labels.xlf file and in the config.yaml are considered. The labels.xlf file has precedence over inline labels in the YAML definition. Optional keys like descriptions or labels for existing fields will only be be generated if they have been set manually. Custom translations, which don't belong to the automatic language keys, will be kept and appended to the end.

Arguments

content-block

content-block
Required

true (false if --extension provided)

Type

string

The Content Block to generate the xlf for.

Options

print

print
Shortcut

p

Type

bool

Print labels.xlf to terminal instead of writing to file system.

extension

extension
Shortcut

e

Type

string

Write labels.xlf to all Content Blocks within the given extension.

Write up-to-date labels.xlf file for Content Block example/example.

vendor/bin/typo3 content-blocks:language:generate example/example
Copied!

Update all labels.xlf files within the extension "site_package".

vendor/bin/typo3 content-blocks:language:generate example/example --extension="site_package"
Copied!

Print up-to-date labels.xlf content for Content Block example/example.

vendor/bin/typo3 content-blocks:language:generate example/example --print
Copied!

Example output:

<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file datatype="plaintext" original="labels.xlf" source-language="en" date="2023-12-03T08:37:53+00:00" product-name="demo/demo">
        <header/>
        <body>
            <trans-unit id="title">
                <source>My demo title</source>
            </trans-unit>
            <trans-unit id="description">
                <source>This is just a demo/demo</source>
            </trans-unit>
            <trans-unit id="header.label">
                <source>Existing field override</source>
            </trans-unit>
            <trans-unit id="slug.label">
                <source>My Slug</source>
            </trans-unit>
            <trans-unit id="slug.description">
                <source>My Slug Description</source>
            </trans-unit>
            <trans-unit id="my_collection.label">
                <source>my_collection</source>
            </trans-unit>
            <trans-unit id="my_collection.text.label">
                <source>text</source>
            </trans-unit>
            <trans-unit id="my_collection.my_collection.label">
                <source>my_collection</source>
            </trans-unit>
            <trans-unit id="my_collection.my_collection.text.label">
                <source>text</source>
            </trans-unit>
            <trans-unit id="external_table.label">
                <source>external_table</source>
            </trans-unit>
            <trans-unit id="external_table_2.label">
                <source>external_table_2</source>
            </trans-unit>
            <trans-unit id="related_content.label">
                <source>related_content</source>
            </trans-unit>
            <trans-unit id="my-custom-key">
                <source>My translation</source>
            </trans-unit>
        </body>
    </file>
</xliff>
Copied!

Publish Assets Command

The command content-blocks:assets:publish publishes your public Content Block assets into the Resources/Public folder of the host extension. Normally, this is performed automatically every time Content Blocks is compiled. In some deployment scenarios this command could be performed in the CI pipeline to publish assets without the requirement for a database connection.

YAML reference

The heart of a Content Block is the config.yaml file. Here you can find all possible configuration options. There are slight differences, whether you are dealing with Content Elements, Page Types or Record Types. In general Content Elements and Page Types are a special concept in TYPO3. The Core already defines the table names, the type field, etc. You just have to define a new type. This is done by providing the name attribute, which will be converted to the type name. Page Types require an integer value for the type. Therefore you need to set it additionally with typeName.

With TYPO3 you can also create custom Record Types. They require you to define a custom table and a labelField field. Per default all extra features like workspaces, language support, frontend restrictions, etc. are enabled. You can selectively disable each one of them, if you don't use them.

Full examples can be found in the examples repository: https://github.com/friendsoftypo3/content-blocks/tree/main/Build/content-blocks-examples

Table of Contents

Common root options

Name Type Default Required
string true
string
boolean true
string full
string
integer "0"
array
array

name

name
Type
string
Required

true

Every editing interface configuration must contain exactly one name. The name is made up of vendor and content block name separated by a / just like the vendor/package notation in a traditional composer.json file. It must be unique and must have at least 3 characters. Both parts, the vendor and content block name, have to be lowercase and be separated by a dash -.

name: my-vendor/my-content-block-name
Copied!

title

title
Type
string

This is the title of the Content Block. If you have a labels.xlf file, you should define it there with the key title. If both are defined, the translation file has precedence. If nothing is defined, the title falls back to name.

title: "My super duper Content Block"
Copied!

prefixFields

prefixFields
Type
boolean
Default
true

The default behavior is to convert the both name parts into a prefix. All dashes are removed in this process and the parts are combined with an underscore to prevent collisions. In order to better reuse fields between Content Blocks, it can be useful to deactivate this option.

prefixFields: false
Copied!

Read more about prefixing.

prefixType

prefixType
Type
string
Default
full

Determines how to prefix the field if prefixFields is enabled. Can be either full (default) or vendor. The latter removes the second part of name from the prefix.

prefixFields: true
prefixType: vendor
Copied!

Read more about prefixing.

vendorPrefix

vendorPrefix
Type
string

If set, this prefix will be used instead of the vendor part of name. This is especially useful if you want to adhere to the best practice of prefixing fields with tx_extension.

vendorPrefix: tx_sitepackage
Copied!

Read more about prefixing.

priority

priority
Type
integer
Default
"0"

The priority can be used to prioritize certain Content Blocks in the loading order. Higher priorities will be loaded before lower ones. This affects e.g. the order in the "New Content Element Wizard".

# This Content Block will be displayed before others without a priority set.
priority: 10
Copied!

basics

basics
Type
array

Globally defined basics are appended to the very end of your fields array. Most commonly used to include shared Tabs.

basics:
    - TYPO3/Appearance
    - TYPO3/Links
Copied!

Can also be used as a Field Type Basic.

Learn more about the concept of Basics.

fields

fields
Type
array

The main entry point for the field definitions. Fields defined in this array are displayed in the backend exactly in the same order. You can create new custom fields or reuse existing ones, which are defined via TCA. Learn here what is needed to define a field.

fields:
    - identifier: my_field
      type: Text
Copied!

ContentElements

Folder: ContentBlocks/ContentElements

EXT:your_extension/ContentBlocks/ContentElements/cta/config.yaml
name: example/cta
fields:
  - identifier: header
    useExistingField: true
Copied!

Learn more about Content Elements.

Options

Here you can find all common root options.

Name Type Default Required
string
string default
string automatically generated from name
bool false

description

description
Type
string

This is the description of the Content Element. If you have a labels.xlf file, you should define it there with the key description. If both are defined, the translation file has precedence.

description: "Here comes my description"
Copied!

group

group
Type
string
Default
default

The group is used for the grouping of the record type selector in the edit view of records. In addition, it is used for the "New Content Element Wizard" for the tab grouping. By default, all new types are placed in the default group.

group: special
Copied!

The Core defines these groups for Content Elements:

  • default
  • menu
  • special
  • forms
  • plugins

typeName

typeName
Type
string
Default
automatically generated from name

The identifier of the new Content Element. It is automatically generated from the name, if not defined manually.

typeName: my_content_element
Copied!

saveAndClose

saveAndClose
Type
bool
Default
false

Can be activated in order to skip the edit view when adding the Content Element via the NewContentElementWizard. This can be useful if you have a Content Element or Plugin without configuration.

saveAndClose: true
Copied!

PageTypes

Folder: ContentBlocks/PageTypes

EXT:your_extension/ContentBlocks/PageTypes/blog/config.yaml
name: example/blog
typeName: 1701284006
fields:
  - identifier: additional_field
    type: Text
Copied!

Learn more about Page Types.

Options

Here you can find all common root options.

Name Type Default Required
integer true
string default

typeName

typeName
Type
integer
Required

true

The typeName has to be a numerical value. There are some reserved numbers, which you can't use either: 199, 254.

typeName: 1701284021
Copied!

group

group
Type
string
Default
default

The group is used for the grouping of the record type selector in the edit view of records. In addition, it is used for the "Create multiple pages" view for selecting the type. By default, all new types are placed in the default group.

group: special
Copied!

The Core defines these groups for Page Types:

  • default
  • link
  • special

RecordTypes

Folder: ContentBlocks/RecordTypes

EXT:your_extension/ContentBlocks/RecordTypes/my-record-type/config.yaml
name: example/my-record-type
table: tx_vendor_my_record_type
labelField: title
fields:
  - identifier: title
    type: Text
Copied!

Learn more about Record Types.

Options

Here you can find all common root options.

Name Type Default Required
string true
string
string|array true
array
string
string automatically generated from name
boolean true
boolean true
boolean true
array true (for all sub properties)
boolean true
boolean true
boolean true
boolean true
string|array
boolean false
string onlyOnPages
array
boolean false
boolean false
boolean false
string

table

table
Type
string
Required

true

The custom table name to be used for the new Record Type.

table: tx_vendor_my_custom_table_name
Copied!

group

group
Type
string

The group is used for the grouping of the record type selector in the edit view of records. By default records are not grouped.

group: my_group
Copied!

labelField

labelField
Type
string|array
Required

true

Defines which field should be used as the title of the record. If not defined, the first valid child field will be used as the label. It is possible to define an array of fields, which will be displayed comma-separated in the backend.

# a single field for the label
labelField: title

# multiple fields will be displayed comma-separated
labelField:
    - title
    - text
Copied!

fallbackLabelFields

fallbackLabelFields
Type
array

Defines which fields should be used as fallback, if labelField is not filled. The first filled field which is found will be used. Can only be used if there is only one labelField field defined.

# fallback fields will be used, if title from labelField is empty
labelField: title
fallbackLabelFields:
    - text1
    - text2
Copied!

typeField

typeField
Type
string

The field identifier to use as the type switch. This field will be automatically generated and prepended as the very first field. The item list is filled automatically as well. There is no need to define this field manually in your fields list. Useful, if you want to define multiple types for a single table (single table inheritance).

typeField: type
Copied!

typeName

typeName
Type
string
Default
automatically generated from name

The identifier of the new Record Type. It is automatically generated from the name, if not defined manually.

typeName: type1
Copied!

languageAware

languageAware
Type
boolean
Default
true

If set to false, language related fields are not created. Namely sys_language_uid, l10n_parent, l10n_source and l10n_diffsource.

# disable language support
languageAware: false
Copied!

workspaceAware

workspaceAware
Type
boolean
Default
true

Creates workspace related fields. Namely t3ver_oid, t3ver_wsid, t3ver_state and t3ver_stage. If EXT:workspaces is not installed, these fields won't be created.

# disable workspaces support
workspaceAware: false
Copied!

editLocking

editLocking
Type
boolean
Default
true

If set to false, the functionality to lock the editing for editors is removed. This refers to the editlock field.

# disable edit lock field
editLocking: false
Copied!

restriction

restriction
Type
array
Default
true (for all sub properties)

There are several restrictions in TYPO3, which filter records by certain constraints.

disabled
Adds a checkbox to hide the record in the frontend.
startTime
Adds a date picker to set the start time when to display the record.
endTime
Adds a date picker to set the end time when to stop displaying the record.
userGroup
Adds a selection to choose user groups, which are allowed to view the record.
restriction:
  disabled: false
  startTime: true
  endTime: true
  userGroup: false
Copied!

softDelete

softDelete
Type
boolean
Default
true

When deleting records in the TYPO3 backend, they are not really deleted in the database. They are merely flagged as deleted. Disabling this option, removes this safety net.

# records will be really deleted in the backend
softDelete: false
Copied!

trackCreationDate

trackCreationDate
Type
boolean
Default
true

Tracks the timestamp of the creation date. Disabling this option removes this information.

trackCreationDate: false
Copied!

trackUpdateDate

trackUpdateDate
Type
boolean
Default
true

Tracks the timestamp of the last update. Disabling this option removes this information.

trackUpdateDate: false
Copied!

sortable

sortable
Type
boolean
Default
true

Tracks the order of records. Arrows will appear to sort records explicitly. Disabling this option removes this functionality. This corresponds to the TCA option sortby.

sortable: false
Copied!

sortField

sortField
Type
string|array

The field identifier to use for sorting records. If set, this will disable the sortable option automatically. This corresponds to the TCA option default_sortby. It is possible to define multiple sorting fields with an array.

# simple sort by one field in ascending order
sortField: title

# sorting by multiple fields with different orders
sortField:
  - identifier: title
    order: desc
  - identifier: text
    order: asc
Copied!

internalDescription

internalDescription
Type
boolean
Default
false

If enabled, this adds a new tab Notes with a description field. When filled with text, a record information will be displayed in the editing view. This corresponds with the TCA ctrl option descriptionColumn. This field is supposed to be used only for the backend.

internalDescription: true
Copied!

rootLevelType

rootLevelType
Type
string
Default
onlyOnPages

Restricts the place, where the record can be created. Possible values are onlyOnPages (default), onlyOnRootLevel and both. This corresponds to the TCA ctrl option rootLevel.

rootLevelType: 'onlyOnRootLevel'
Copied!

security

security
Type
array
ignoreWebMountRestriction
default false, Allows users to access records that are not in their defined web-mount, thus bypassing this restriction.
ignoreRootLevelRestriction
default false, Allows non-admin users to access records that are on the root-level (page ID 0), thus bypassing this usual restriction.
ignorePageTypeRestriction
default false (but true if table is used as foreign_table), Allows to use the record on any kind of page type.
security:
    ignoreWebMountRestriction: true
    ignoreRootLevelRestriction: true
    ignorePageTypeRestriction: true
Copied!

readOnly

readOnly
Type
boolean
Default
false

If enabled, the record can not be edited in the TYPO3 backend anymore.

readOnly: true
Copied!

adminOnly

adminOnly
Type
boolean
Default
false

If enabled, only admins can edit the record.

adminOnly: true
Copied!

hideAtCopy

hideAtCopy
Type
boolean
Default
false

If enabled, the record will be disabled, when copied. Only works, if restriction.disabled is set to true.

hideAtCopy: true
Copied!

appendLabelAtCopy

appendLabelAtCopy
Type
string

If set, the label field labelField will be appended with this string, when copied.

appendLabelAtCopy: append me
Copied!

FileTypes

New in version 1.2

Folder: ContentBlocks/FileTypes

EXT:your_extension/ContentBlocks/FileTypes/image/config.yaml
name: example/file-type-image
typeName: image
prefixFields: false
fields:
  - identifier: image_overlay_palette
    type: Palette
    label: 'LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette'
    fields:
      - identifier: alternative
        useExistingField: true
      - identifier: description
        useExistingField: true
      - type: Linebreak
      - identifier: link
        useExistingField: true
      - identifier: title
        useExistingField: true
      - type: Linebreak
      - identifier: example_custom_field
        type: Text
        label: 'My custom Field'
      - type: Linebreak
      - identifier: crop
        useExistingField: true
Copied!

Learn more about File Types.

Options

Name Type Default Required
string true

typeName

typeName
Type
string
Required

true

The typeName has to be one of these keywords:

  • text
  • image
  • audio
  • video
  • application
typeName: image
Copied!

Field Types

These are the Content Blocks Field Types, which you can use out of the box. They cover mostly basic types. Custom Field Types can be developed from scratch. More semantic field types may be added over time.

If you are familiar with TCA types, then you will probably recognize most of these types. Some types have been renamed for better clarity.

Simple Field Types:

Relational Field Types:

Structural Field Types:

Special field types:

Common field options

Field options, which can be defined inside the fields array.

Name Type Default Required
string true
string true
string
string
bool false
boolean true
string full
string|array
string

identifier

identifier
Type
string
Required

true

The field's identifier has to be unique within a Content Block. Exception is within a collections' field array, as this starts a new scope.

fields:
    identifier: my_identifier
    type: Text
Copied!

type

type
Type
string
Required

true

The field's type. See Field Types.

fields:
    identifier: my_identifier
    type: Text
Copied!

label

label
Type
string

By default labels should be defined inside the labels.xlf file. But in case there is only one language for the backend you may define labels directly in the YAML configuration. Translation files have precedence over this.

fields:
    identifier: my_identifier
    type: Text
    label: Static label
Copied!

description

description
Type
string

The same as for label above.

fields:
    identifier: my_identifier
    type: Text
    description: Static description
Copied!

useExistingField

useExistingField
Type
bool
Default
false

If set to true, the identifier is treated as an existing field from the Core or your own defined field in TCA. To learn more about reusing fields read this article.

fields:
    identifier: bodytext
    useExistingField: true
Copied!

prefixField

prefixField
Type
boolean
Default
true

If set to false, the prefixing is disabled for this field. This overrules the global option prefixFields.

fields:
    identifier: my_identifier
    type: Text
    prefixField: false
Copied!

Read more about prefixing.

prefixType

prefixType
Type
string
Default
full

Determines how to prefix the field if local prefixField or global prefixFields is enabled. Can be either full (default) or vendor.

fields:
    identifier: my_identifier
    type: Text
    prefixField: true
    prefixType: vendor
Copied!

Read more about prefixing.

displayCond

displayCond
Type
string|array

Can be used to display the field only under certain conditions. Please have a look at the official documentation for more information.

# Simple, only one rule.
displayCond: 'FIELD:identifier:=:value'
Copied!
# Multiple rules combined with AND.
displayCond:
  AND:
    - 'FIELD:identifier:=:value'
    - 'FIELD:another_identifier:=:1'
Copied!

onChange

onChange
Type
string

Can be used to trigger a reload of the Content Type when this specific field is changed. Should be used, if a rule of displayCond is used for this field.

onChange: reload
Copied!

Basic

The Basic type can be used to include a pre-defined set of fields. Most commonly used to include shared Palettes.

Can also be used as a root option basics.

Read the main article about Basics.

Example:

EXT:your_extension/ContentBlocks/ContentElements/basics/config.yaml
name: example/basics
fields:
    - identifier: TYPO3/Header
      type: Basic
Copied!

Category

The Category type can handle relations to categories. The categories are taken from the system table sys_categories.

Settings

Name Type Default Required
string
integer "0"
integer "0"
string

relationship

relationship
Type
string

Depending on the relationship, the category relations is stored (internally) in a different way. Possible keywords are oneToOne, oneToMany or manyToMany (default).

maxitems

maxitems
Type
integer
Default
"0"

Maximum number of items. Defaults to a high value. JavaScript record validation prevents the record from being saved if the limit is not satisfied.

minitems

minitems
Type
integer
Default
"0"

Minimum number of items. Defaults to 0. JavaScript record validation prevents the record from being saved if the limit is not satisfied. The field can be set as required by setting minitems to at least 1.

treeConfig.startingPoints

treeConfig.startingPoints
Type
string

Allows to set one or more roots (category uids), from which the categories should be taken from.

Examples

Minimal

name: example/category
fields:
  - identifier: categories
    type: Category
Copied!

Advanced / use case

name: example/category
fields:
  - identifier: categories
    type: Category
    minitems: 1
    treeConfig:
      startingPoints: 7
    relationship: oneToOne
Copied!

Checkbox

The Checkbox type generates one or more checkbox fields.

Settings

Name Type Default Required
integer (bit value) "0"
array
string check
array ["itemsProcConfig"]

default

default
Type
integer (bit value)
Default
"0"

The default value corresponds to a bit value. If you only have one checkbox having 1 or 0 will work to turn it on or off by default. For more than one checkbox you need to calculate the bit representation.

items

items
Type
array

Only necessary if more than one checkbox is desired. Contains the checkbox elements as separate array items. The label can also be defined as a LLL-reference.

Example:

items:
  - label: 'The first'
  - label: 'The second'
  - label: 'The third'
Copied!

XLF translation keys for items have the following convention:

<body>
    <trans-unit id="FIELD_IDENTIFIER.items.0.label">
        <source>Label for first Checkbox item</source>
    </trans-unit>
    <trans-unit id="FIELD_IDENTIFIER.items.1.label">
        <source>Label for second Checkbox item</source>
    </trans-unit>
    <trans-unit id="FIELD_IDENTIFIER.items.n.label">
        <source>Label for nth Checkbox item</source>
    </trans-unit>
</body>
Copied!

renderType

renderType
Type
string
Default
check
  • checkboxToggle
  • checkboxLabeledToggle

allowedCustomProperties

allowedCustomProperties
Type
array
Default
["itemsProcConfig"]

Sometimes it is needed to provide custom configuration for the itemsProcFunc functionality. These extra properties need to be explicitly allowed via this option. This option receives an array of those strings. By default, the custom option itemsProcConfig is allowed.

Examples

Minimal

name: example/checkbox
fields:
  - identifier: checkbox
    type: Checkbox
Copied!

Advanced / use case

Multiple checkboxes:

name: example/checkbox
fields:
  - identifier: checkbox
    type: Checkbox
    items:
      - label: 'The first'
      - label: 'The second'
      - label: 'The third'
    default: 2
    cols: 3
Copied!

Toggle checkbox:

name: example/checkbox
fields:
  - identifier: toggle
    type: Checkbox
    renderType: checkboxToggle
    default: 1
Copied!

Labeled toggle checkbox:

name: example/checkbox
fields:
  - identifier: toggle
    type: Checkbox
    renderType: checkboxLabeledToggle
    items:
      - label: 'Your label'
        labelChecked: 'Label checked'
        labelUnchecked: 'Label unchecked'
        invertStateDisplay: true
Copied!

Collection

The Collection type generates a field for Inline-Relational-Record-Editing (IRRE), which allows nesting of other field types. This field type allows building structures like image sliders, accordions, tabs and so on.

Collections will automatically create custom tables and use the identifier as table name. It is possible to override this with the setting table. Collections are always hidden in the List module. Usually Collections only have one type. To realise multiple types it is recommended to extract the definition to a separate Record Type and use foreign_table instead.

Custom icon

In order to define a custom icon for your Collection field, you may place an image file inside assets folder called {identifier}.svg. So for example if your identifier for the Collection is my_collection, then your image should be named my_collection.svg. Alternatively, you can also provide png or gif files. These should be 64x64px.

Settings

Name Type Default Required
string|array true
array
string
array true
integer "0"
integer "0"
string oneToMany
bool|null null
string top
string (table)
string (field)
boolean false
boolean false
array []
array []

labelField

labelField
Type
string|array
Required

true

Defines which field should be used as the title of the record. If not defined, the first valid child field will be used as the label. It is possible to define an array of fields, which will be displayed comma-separated in the backend.

# a single field for the label
labelField: title

# multiple fields will be displayed comma-separated
labelField:
    - title
    - text
Copied!

fallbackLabelFields

fallbackLabelFields
Type
array

Defines which fields should be used as fallback, if labelField is not filled. The first filled field which is found will be used. Can only be used if there is only one labelField field defined.

# fallback fields will be used, if title from labelField is empty
labelField: title
fallbackLabelFields:
    - text1
    - text2
Copied!

table

table
Type
string

Alternative table name for the Collection. Default is identifier with prefix if enabled.

table: tx_vendor_my_custom_table_name
Copied!

fields

fields
Type
array
Required

true

Configures a set of fields as repeatable child objects. All fields defined in field types are possible as children. It is also possible to further nest Collection fields.

Example:

fields:
  - identifier: text
    type: Text
  - identifier: image
    type: File
Copied!

minitems

minitems
Type
integer
Default
"0"

Minimum number of child items. Defaults to 0. JavaScript record validation prevents the record from being saved if the limit is not satisfied.

maxitems

maxitems
Type
integer
Default
"0"

Maximum number of child items. Defaults to a high value. JavaScript record validation prevents the record from being saved if the limit is not satisfied.

relationship

relationship
Type
string
Default
oneToMany

The relationship defines the cardinality between the relations. Possible values are oneToMany (default), manyToOne and oneToOne. In case of a [x]toOne relation, the processed field will be filled directly with the record instead of a collection of records. In addition, maxitems will be automatically set to 1.

appearance.collapseAll

appearance.collapseAll
Type
bool|null
Default
null
  • Default (null): Last collapsed/expanded state is remembered
  • true: Show all child records collapsed
  • false: Show all child records expanded

appearance.levelLinksPosition

appearance.levelLinksPosition
Type
string
Default
top

Defines where to show the "New record" link in relation to the child records. Valid keywords are top, bottom and both.

foreign_table

foreign_table
Type
string (table)

It is possible to reference another table instead of creating a new one. This table can be defined by another Content Block, but can also be an existing table defined by the Core or another extension.

foreign_field

foreign_field
Type
string (field)

It is possible to override the field name pointing to the parent record. Per default it is called foreign_table_parent_uid. This corresponds with the TCA option foreign_field.

shareAcrossTables

shareAcrossTables
Type
boolean
Default
false

Allows to reference a Record Type across multiple tables, if foreign_table is used.

Make sure to add this to every Collection, which shares the table.

This will create a new field called tablenames. It corresponds to the TCA option foreign_table_field. The field name can be overridden by defining foreign_table_field explicitly.

shareAcrossFields

shareAcrossFields
Type
boolean
Default
false

Allows to reference a Record Type across multiple fields, if foreign_table is used.

Make sure to add this to every Collection, which shares the table.

This will create a new field called fieldname. It corresponds to the TCA option foreign_match_fields.

allowedRecordTypes

allowedRecordTypes
Type
array
Default
[]

This option allows you to restrict possible record types for the type selector of the child record. The order of definition is used to sort the items. The first item in the list will always be the default type.

allowedRecordTypes:
  - text
  - images
Copied!

overrideType

overrideType
Type
array
Default
[]

Type Overrides can be used to override the Record Definition in the context of as single field. This option only makes sense, if you don't define fields, but an external foreign_table, that you want to override. Refer to the API documentation if you want to learn more.

name: friendsoftypo3/example
table: tx_friendsoftypo3_example
prefixFields: false
labelField: title
fields:
  -
    identifier: title
    type: Text
    label: Title
  - identifier: collection_override
    type: Collection
    foreign_table: tx_hov_domain_model_record1
    overrideType:
      record1:
        - identifier: type
          type: Select
          useExistingField: true
        - identifier: title
          type: Text
          useExistingField: true
        - identifier: custom_field
          type: Text
Copied!

Example

Minimal

name: example/collection
fields:
  - identifier: collection
    type: Collection
    labelField: text
    fields:
      - identifier: text
        type: Text
Copied!

Advanced / use case

name: example/collection
fields:
  - identifier: slides
    type: Collection
    labelField: title
    maxitems: 5
    minitems: 1
    appearance:
      collapseAll: true
      levelLinksPosition: both
    fields:
      - identifier: image
        type: File
        minitems: 1
        maxitems: 1
      - identifier: title
        type: Text
Copied!

This custom table my_slide needs to be defined as a Record Type in order to be used as a foreign table in slides.

name: example/slide
table: my_slide
labelField: title
fields:
  - identifier: title
    type: Text
  - identifier: image
    type: File
Copied!
name: example/collection
fields:
  - identifier: slides
    type: Collection
    foreign_table: my_slide
    shareAcrossTables: true
    shareAcrossFields: true
Copied!

Color

The Color type provides a simple color picker.

Settings

Name Type Default Required
string ''
boolean false
boolean false
array

default

default
Type
string
Default
''

Default value set if a new record is created.

required

required
Type
boolean
Default
false

If set, the field will become mandatory.

opacity

opacity
Type
boolean
Default
false

Enables selection of opacity for the color.

valuePicker

valuePicker
Type
array

Renders a select box with static values next to the input field. When a value is selected in the box, the value is transferred to the field. Keys:

items (array)
An array with selectable items. Each item is an array with the first being the label in the select drop-down (LLL reference possible), and the second being the value transferred to the input field.

Example:

valuePicker:
  items:
    - [ 'Red', '#FF0000' ]
    - [ 'Green', '#008000' ]
    - [ 'Blue', '#0000FF' ]
Copied!

Example

Minimal

name: example/color
fields:
  - identifier: color
    type: Color
Copied!

Advanced / use case

name: example/color
fields:
  - identifier: color
    type: Color
    autocomplete: true
    default: '#0000FF'
    valuePicker:
      items:
        - [ 'Red', '#FF0000' ]
        - [ 'Green', '#008000' ]
        - [ 'Blue', '#0000FF' ]
Copied!

DateTime

The DateTime type provides a date picker. If not configured otherwise, the value is stored as a timestamp.

Settings

Name Type Default Required
string ''
string ''
string ''
array
boolean false
boolean false

default

default
Type
string
Default
''

Default value in Y-m-d format. Set if a new record is created. For example 2023-01-01.

format

format
Type
string
Default
''

Defines how the date should be formatted in the backend. Possible values are datetime, date or time and timesec.

dbType

dbType
Type
string
Default
''

This option changes the date field to a native MySql DATETIME, DATE or TIME field. Possible values are datetime, date or time respectively.

range

range
Type
array

An array which defines an integer range within which the value must be. Keys:

lower (string in format Y-m-d H:i:s)
Defines the min date.
upper (string in format Y-m-d H:i:s)
Defines the max date.

It is allowed to specify only one of both of them.

Example:

range:
  lower: '2020-01-01'
  upper: '2020-12-31'
Copied!

disableAgeDisplay

disableAgeDisplay
Type
boolean
Default
false

Disable the display of the age in the backend view.

required

required
Type
boolean
Default
false

If set, the field becomes mandatory.

Examples

Minimal

name: example/datetime
fields:
  - identifier: datetime
    type: DateTime
    format: date
Copied!
name: example/datetime
fields:
  - identifier: datetime
    type: DateTime
    format: datetime
    dbType: datetime
Copied!

Advanced / use case

name: example/datetime
fields:
  - identifier: datetime
    type: DateTime
    format: datetime
    default: '2023-02-11 12:00:00'
    disableAgeDisplay: true
    size: 20
    range:
      lower: '2019-01-31 12:00:00'
      upper: '2040-01-31 12:00:00'
    required: true
Copied!

Email

The Email type creates an input field, which is validated against an email pattern. If the input does not contain a valid email address, a flash message warning will be displayed.

Settings

Name Type Default Required
string ''
string ''
boolean false

default

default
Type
string
Default
''

Default value set if a new record is created.

placeholder

placeholder
Type
string
Default
''

Placeholder text for the field.

required

required
Type
boolean
Default
false

If set, the field becomes mandatory.

Example

Minimal

name: example/email
fields:
  - identifier: email
    type: Email
Copied!

Advanced / use case

name: example/email
fields:
  - identifier: email
    type: Email
    autocomplete: true
    default: 'developer@localhost.de'
    placeholder: 'Enter your email address'
    required: true
Copied!

File

The File type generates a field for file relations.

Settings

Name Type Default Required
boolean true
string|array ''
integer 99999
integer "0"
string oneToMany
array []
array []

extendedPalette

extendedPalette
Type
boolean
Default
true

If enabled, an additional image or media palette will be rendered. For image files it consists of the additional fields crop, alternative and link. For audio and media files an additional autoplay field is added. For other file types, like plain text, this option has no effect. Disable this option, if you don't need these additional fields.

allowed

allowed
Type
string|array
Default
''

Possible values: common-image-types, common-media-types or your custom list of file types.

maxitems

maxitems
Type
integer
Default
99999

Maximum number of child items. Defaults to a high value. JavaScript record validation prevents the record from being saved if the limit is not satisfied.

minitems

minitems
Type
integer
Default
"0"

Minimum number of child items. Defaults to 0. JavaScript record validation prevents the record from being saved if the limit is not satisfied. The field can be set as required by setting minitems to at least 1.

relationship

relationship
Type
string
Default
oneToMany

The relationship defines the cardinality between the relations. Possible values are oneToMany (default), manyToOne and oneToOne. In case of a [x]toOne relation, the processed field will be filled directly with the file reference instead of a collection of file references. In addition, maxitems will be automatically set to 1.

cropVariants

cropVariants
Type
array
Default
[]

It is possible to define crop variants for this specific field and Content Block. This documentation only covers the most basic configuration. Refer to the TCA documentation for a complete overview of possibilities.

Example configuration below. The aspect ratios can be defined as a float value or a fraction. Only the simple division operation a / b is allowed.

cropVariants:
  teaser:
    title: Teaser
    allowedAspectRatios:
      portrait:
        title: Portrait
        value: 0.75
      landscape:
        title: Landscape
        value: 4 / 3
Copied!

overrideType

overrideType
Type
array
Default
[]

Type Overrides can be used to override the File Definition in the context of as single field. Refer to the API documentation if you want to learn more.

overrideType:
  image:
    - identifier: image_overlay_palette
      type: Palette
      label: 'LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette'
      fields:
        - identifier: alternative
          useExistingField: true
        - identifier: description
          useExistingField: true
        - type: Linebreak
        - identifier: link
          useExistingField: true
        - identifier: title
          useExistingField: true
        - type: Linebreak
        - identifier: example_custom_field
          type: Text
          label: 'My custom Field'
        - type: Linebreak
        - identifier: crop
          useExistingField: true
Copied!

Example

Minimal

All file types allowed, no restrictions.

name: example/file
fields:
  - identifier: my_file_field
    type: File
Copied!

Advanced / use case

Allow only image types, disable extended palette (no cropping field), require at least one image and set limit to 10 images.

name: example/image
fields:
  - identifier: image
    type: File
    extendedPalette: false
    minitems: 1
    maxitems: 10
    allowed: common-image-types
Copied!

Allow media types like audio, video and youtube (or vimeo).

name: example/media
fields:
  - identifier: media
    type: File
    allowed: common-media-types
Copied!

Set specific crop variants for an image field.

name: example/image
fields:
  - identifier: image
    type: File
    allowed: common-image-types
    cropVariants:
      desktop:
        title: Desktop
        allowedAspectRatios:
          portrait:
            title: Portrait
            value: 0.75
          landscape:
            title: Landscape
            value: 4 / 3
        focusArea:
          x: 0.3
          y: 0.3
          width: 0.4
          height: 0.4
        coverAreas:
          - x: 0.1
            y: 0.8
            width: 0.8
            height: 0.1
      tablet:
        title: Tablet
        allowedAspectRatios:
          square:
            title: Square
            value: 0.75
      smartphone:
        title: Smartphone
        allowedAspectRatios:
          landscape:
            title: Landscape
            value: 4 / 3
Copied!

Usage in Fluid

<f:for each="{data.image}" as="image">
    <f:image image="{image}" width="120" maxHeight="100"/>
</f:for>
Copied!

FlexForm

The FlexForm field allows you to group multiple fields into one database column. It is mostly used to store configuration options, rather than actual content. By using FlexForm it is possible to conserve the database, but it also has its limitations. For example nesting of Collections is disallowed.

Settings

fields

fields
Type
array
Required

true

Fields to be used inside the FlexForm definition.

Sheets, Sections and Containers

FlexForm provides a way to organize your fields in Sheets. Sheets create a tab navigation in the editing interface. Learn more about Sheets.

An alternative way of defining repeating content are Sections. Sections can have multiple Containers. Learn more about Sections.

Examples

Minimal

name: example/flex
fields:
  - identifier: my_flexform
    type: FlexForm
    fields:
      - identifier: header
        type: Text
      - identifier: check
        type: Checkbox
Copied!

With Sheets

name: example/flex
fields:
  - identifier: my_flexform
    type: FlexForm
    fields:
      - identifier: sheet1
        type: Sheet
        label: Sheet 1
        description: Description for Sheet 1
        linkTitle: Link title for Sheet 1
        fields:
          - identifier: header
            type: Text
          - identifier: check
            type: Checkbox
      - identifier: sheet2
        type: Sheet
        label: Sheet 2
        fields:
          - identifier: link
            type: Link
          - identifier: radio
            type: Radio
            default: 0
            items:
              - label: Option 1
                value: 0
              - label: Option2 2
                value: 1
Copied!

With Sections and Containers

name: cbteam/flexform
fields:
  - identifier: pi_flexform
    useExistingField: true
    label: My FlexForm field
    fields:
      - type: Sheet
        identifier: sheet1
        label: Sheet 1
        fields:
          - identifier: link1
            type: Link
          - identifier: section1
            type: Section
            label: Section 1
            container:
              - identifier: container1
                label: Container 1
                fields:
                  - identifier: container_field
                    type: Text
              - identifier: container2
                label: Container 2
                fields:
                  - identifier: container_field2
                    type: Textarea
      - type: Sheet
        identifier: sheet2
        label: Sheet 2
        fields:
          - identifier: header2
            type: Text
          - identifier: textarea2
            type: Textarea
          - identifier: header1
            type: Text
Copied!

Sheet

type: Sheet

Sheets are used to further group FlexForm fields into separate tabs. Note that you need at least 2 Sheets for a tab navigation to appear in the backend. This is purely cosmetical and, like Palettes and Tabs, has no effect on frontend rendering.

Settings

Name Type Default Required
string true
string
string
string

identifier

identifier
Type
string
Required

true

A unique identifier

label

label
Type
string

Define a label. If not defined, identifier is used as fallback.

description

description
Type
string

Define a description.

linkTitle

linkTitle
Type
string

The link title is displayed when hovering over the tab.

Example:

name: example/flex
fields:
  - identifier: my_flexform
    type: FlexForm
    fields:
      - identifier: sheet1
        type: Sheet
        label: Sheet 1
        description: Description for Sheet 1
        linkTitle: Link title for Sheet 1
        fields:
          - identifier: header
            type: Text
      - identifier: sheet2
        type: Sheet
        label: Sheet 2
        fields:
          - identifier: link
            type: Link
Copied!

Labels

XLF translation keys for Sheets have the following convention:

<body>
    <trans-unit id="FIELD_IDENTIFIER.sheets.SHEET_IDENTIFIER.label">
        <source>Label for Sheet</source>
    </trans-unit>
    <trans-unit id="FIELD_IDENTIFIER.sheets.SHEET_IDENTIFIER.description">
        <source>Description for Sheet</source>
    </trans-unit>
    <trans-unit id="FIELD_IDENTIFIER.sheets.SHEET_IDENTIFIER.linkTitle">
        <source>Link title for Sheet</source>
    </trans-unit>
</body>
Copied!

Section

type: Section

Sections are like Collections for FlexForm. The difference is, that you can't reference foreign types. Instead you define anonymous structures, which are only available for this specific FlexForm field.

A Section requires you to define at least one Container, which holds the available fields. You can have multiple Containers with different fields. If you define more than one Container, the editing interface will display multiple buttons to choose from.

Settings

Name Type Default Required
string true
string
array true

identifier

identifier
Type
string
Required

true

A unique identifier

label

label
Type
string

Define a label. If not defined, identifier is used as fallback.

container

container
Type
array
Required

true

Define one or more Containers with fields.

identifier
A unique identifier
label
Define a label. If not defined, identifier is used as fallback.
fields
Define available fields. These field types are prohibited: FlexForm, File, Collection.

Example:

- identifier: pi_flexform
  useExistingField: true
  fields:
    - identifier: section1
      type: Section
      label: Section 1
      container:
        - identifier: container1
          label: Container 1
          fields:
            - identifier: container_field
              type: Text
              label: Container field
        - identifier: container2
          label: Container 2
          fields:
            - identifier: container_field2
              type: Textarea
              label: Container field 2
Copied!

Labels

XLF translation keys for Sections and Containers have the following convention:

<body>
    <trans-unit id="FIELD_IDENTIFIER.sections.SECTION_IDENTIFIER.title">
        <source>Label for Section</source>
    </trans-unit>
    <trans-unit id="FIELD_IDENTIFIER.sections.SECTION_IDENTIFIER.container.CONTAINER_IDENTIFIER.title">
        <source>Label for Container</source>
    </trans-unit>
    <trans-unit id="FIELD_IDENTIFIER.sections.SECTION_IDENTIFIER.container.CONTAINER_IDENTIFIER.FIELD_IDENTIFIER.label">
        <source>Label for field in Container</source>
    </trans-unit>
</body>
Copied!

Folder

The Folder type enables to select one or more folders. This field type is resolved to an array of \TYPO3\CMS\Core\Resource\Folder objects.

Settings

Name Type Default Required
array
integer
integer
string oneToMany

elementBrowserEntryPoints

elementBrowserEntryPoints
Type
array

Enables to set an entrypoint, from which to select folders by default.

maxitems

maxitems
Type
integer

Maximum number of items. Defaults to a high value. JavaScript record validation prevents the record from being saved if the limit is not satisfied.

minitems

minitems
Type
integer

Minimum number of items. Defaults to 0. JavaScript record validation prevents the record from being saved if the limit is not satisfied. The field can be set as required by setting minitems to at least 1.

relationship

relationship
Type
string
Default
oneToMany

The relationship defines the cardinality between the relations. Possible values are oneToMany (default), manyToOne and oneToOne. In case of a [x]toOne relation, the processed field will be filled directly with the folder instead of a collection of folders. In addition, maxitems will be automatically set to 1.

Examples

Minimal

name: example/folder
fields:
  - identifier: folder
    type: Folder
Copied!

Advanced / use case

name: example/folder
fields:
  - identifier: folder
    type: Folder
    elementBrowserEntryPoints:
      _default: '1:/styleguide/'
    minitems: 1
Copied!

Usage in Fluid

In most cases you want to retrieve the files within the folders. To achieve this, you have to access the files via the getFiles() method. In Fluid this looks like this for a field with identifier: folder.

<f:for each="{data.folder}" as="folder">
    <f:for each="{folder.files}" as="item">
        <f:image image="{item}" />
    </f:for>
</f:for>
Copied!

Json

The Json type is for a textarea which will be rendered as a json editor.

Settings

Name Type Default Required
integer 30
integer 5
boolean false
boolean false
boolean true

cols

cols
Type
integer
Default
30

Size for the input field. Min value is 10 and max value is 50.

rows

rows
Type
integer
Default
5

Amount of rows for the textarea. Min value is 1 and max value is 20.

required

required
Type
boolean
Default
false

If set, the Json textarea needs to be filled.

readOnly

readOnly
Type
boolean
Default
false

If set, the Json textarea is read only.

enableCodeEditor

enableCodeEditor
Type
boolean
Default
true

In case enableCodeEditor is set to true, which is the default and the system extension t3editor is installed and active, the JSON value is rendered in the corresponding code editor. Otherwise it is rendered in a standard textarea HTML element.

Examples

Minimal

name: example/json
fields:
  - identifier: json
    type: Json
Copied!

Advanced / use case

name: example/json
fields:
  - identifier: json
    type: Json
    required: true
    readOnly: true
    cols: 50
    rows: 10
    enableCodeEditor: false
    placeholder: '[{"foo": "bar"}]'
Copied!

Language

The Language type is for rendering a select box with all available languages for the current installation.

Examples

Minimal

name: example/language
fields:
  - identifier: language
    type: Language
Copied!

Linebreak

The Linebreak field is used inside Palette fields to add a manual linebreak. Otherwise all fields within a Palette are displayed next to each other. Note: Contrary to all other field types, Linebreaks don't need an identifier.

Settings

ignoreIfNotInPalette

ignoreIfNotInPalette
Type
boolean
Default
false

Normally, linebreaks can only be defined inside of a palette. With this flag set to true, linebreaks can also appear outside of palettes (but do nothing). This is especially useful in combination with Basics when you want a set of fields to be used both in palettes and on root level.

Examples

Minimal

name: example/linebreak
fields:
  - identifier: palette_1
    type: Palette
    fields:
      - identifier: number
        type: Number
      - type: Linebreak
      - identifier: text
        type: Text
Copied!

For in-depth information about palettes and linebreaks refer to the TCA documentation.

Number

The Number only allows integers or decimals as input values.

Settings

Name Type Default Required
string 'integer'
integer "0"
array
boolean false
array

format

format
Type
string
Default
'integer'

Possible values: integer (default) or decimal.

default

default
Type
integer
Default
"0"

Default value set if a new record is created.

range

range
Type
array

An array which defines an integer range within the value must be.

lower (integer)
Defines the lower integer value.
upper (integer)
Defines the upper integer value.

Example:

range:
  lower: 10
  upper: 999
Copied!

required

required
Type
boolean
Default
false

If set, the field becomes mandatory.

slider

slider
Type
array

Render a value slider next to the field. Available keys:

step (integer / float)
Set the step size the slider will use. For floating point values this can itself be a floating point value. Default: 1.
width (integer, pixels)
Define the width of the slider. Default: 100.

Example:

slider:
  step: 1
  width: 100
Copied!

Examples

Minimal

name: example/number
fields:
  - identifier: number
    type: Number
Copied!

Advanced / use case

name: example/number
fields:
  - identifier: number
    type: Number
    format: integer
    default: 10
    size: 20
    range:
      lower: 10
      upper: 999
    slider:
      step: 1
      width: 100
    valuePicker:
      items:
        - [ '100', 100 ]
        - [ '250', 250 ]
        - [ '500', 500 ]
Copied!

Palette

The Palette field is used to group other fields. Grouped fields will be displayed next to each other rather than below each other.

Pre-defined palettes as for example the TYPO3/Header palette for Content Elements are available as Basics.

Labels

XLF translation keys for Palettes have the following convention:

<body>
    <trans-unit id="palettes.PALETTE_IDENTIFIER.label">
        <source>Label for Palette</source>
    </trans-unit>
    <trans-unit id="palettes.PALETTE_IDENTIFIER.description">
        <source>Description for Palette</source>
    </trans-unit>
    <trans-unit id="COLLECTION_IDENTIFIER.palettes.PALETTE_IDENTIFIER.label">
        <source>Label for Palette in Collection</source>
    </trans-unit>
    <trans-unit id="COLLECTION_IDENTIFIER1.COLLECTION_IDENTIFIER2.palettes.PALETTE_IDENTIFIER.label">
        <source>Label for Palette in nested Collection</source>
    </trans-unit>
</body>
Copied!

Examples

Minimal

name: example/palette
fields:
  - identifier: palette_1
    type: Palette
    label: Palette 1
    description: My palette description
    fields:
      - identifier: number
        type: Number
      - identifier: text
        type: Text
Copied!

For in-depth information about palettes refer to the TCA documentation.

Pass

The Pass type provides a virtual field, which is not visible in the backend. It is useful for extension logic handling this value independently.

Settings

default

default
Type
mixed
Default
''

Default value set if a new record is created.

Example

Minimal

name: example/pass
fields:
  - identifier: pass
    type: Pass
Copied!

Password

The Password type generates a password field.

Settings

Name Type Default Required
bool true
string
string
boolean false
integer

hashed

hashed
Type
bool
Default
true

Whether the password should be hashed with the configured hashing algorithm. Set this value to false to disable hashing.

passwordPolicy

passwordPolicy
Type
string

The password policy will ensure, that the new password complies with the configured password policy.

Password policy requirements are shown below the password field, when the focus is changed to the password field.

placeholder

placeholder
Type
string

Placeholder text for the field.

required

required
Type
boolean
Default
false

If set, the field becomes mandatory.

size

size
Type
integer

Abstract value for the width of the <input> field.

Examples

Minimal

name: example/password
fields:
  - identifier: password
    type: Password
Copied!

Advanced / use case

name: example/password
fields:
  - identifier: password
    type: Password
    required: true
    hashed: false
    passwordPolicy: 'default'
    fieldControl:
      passwordGenerator:
        renderType: passwordGenerator
Copied!

Radio

The Radio type creates a set of radio buttons. The value is typically stored as integer value, each radio item has one assigned number, but it can be a string, too.

Settings

Name Type Default Required
string|int ''
array true

default

default
Type
string|int
Default
''

Default value set if a new record is created.

items

items
Type
array
Required

true

Contains the radio items. Each item is an array with the keys label and value. Values are usually integers, but can also be strings if desired.

Example:

items:
  - label: 'First option'
    value: 0
  - label: 'Second option'
    value: 1
  - label: 'Third option'
    value: 2
Copied!

XLF translation keys for items have the following convention:

<body>
    <trans-unit id="FIELD_IDENTIFIER.items.1.label">
        <source>Label for item with value 1</source>
    </trans-unit>
    <trans-unit id="FIELD_IDENTIFIER.items.2.label">
        <source>Label for item with value 2</source>
    </trans-unit>
    <trans-unit id="FIELD_IDENTIFIER.items.VALUE.label">
        <source>Label for item with value VALUE</source>
    </trans-unit>
    <trans-unit id="FIELD_IDENTIFIER.items.label">
        <source>Label for item with empty value</source>
    </trans-unit>
</body>
Copied!

allowedCustomProperties

allowedCustomProperties
required

false

type

array

default

["itemsProcConfig"]

Sometimes it is needed to provide custom configuration for the itemsProcFunc functionality. These extra properties need to be explicitly allowed via this option. This option receives an array of those strings. By default, the custom option itemsProcConfig is allowed.

Example

Minimal

name: example/radio
fields:
  - identifier: radioboxes
    type: Radio
    items:
      - label: 'First option'
        value: 0
      - label: 'Second option'
        value: 1
Copied!

Advanced / use case

name: example/radio
fields:
  - identifier: radioboxes
    type: Radio
    default: 'one'
    items:
      - label: 'First option'
        value: 'one'
      - label: 'Second option'
        value: 'two'
      - label: 'Third option'
        value: 'three'
Copied!

Relation

The Relation type can handle relations to other record types. They will be available to select from the Record Selector.

Settings

Name Type Default Required
string (table name, comma-separated) true
integer
integer
string oneToMany

allowed

allowed
Type
string (table name, comma-separated)
Required

true

One or more tables, that should be referenced.

This table can be defined by another Content Block, but can also be an existing table defined by the Core or another extension.

maxitems

maxitems
Type
integer

Maximum number of items. Defaults to a high value. JavaScript record validation prevents the record from being saved if the limit is not satisfied.

minitems

minitems
Type
integer

Minimum number of items. Defaults to 0. JavaScript record validation prevents the record from being saved if the limit is not satisfied. The field can be set as required by setting minitems to at least 1.

relationship

relationship
Type
string
Default
oneToMany

The relationship defines the cardinality between the relations. Possible values are oneToMany (default), manyToOne and oneToOne. In case of a [x]toOne relation, the processed field will be filled directly with the record instead of a collection of records. In addition, maxitems will be automatically set to 1.

Examples

Minimal

name: example/relation
fields:
  - identifier: record_select
    type: Relation
    allowed: 'some_table'
Copied!

Advanced / use case

name: example/relation
fields:
  - identifier: page_select
    type: Relation
    allowed: 'pages'
    maxitems: 1
    suggestOptions:
      default:
        additionalSearchFields: 'nav_title, url'
        addWhere: 'AND pages.doktype = 1'
Copied!

Select

The Select type generates a simple select field.

Settings

Name Type Default Required
string true
array
string
integer
integer
string oneToMany
int 255
array ["itemsProcConfig"]

renderType

renderType
Type
string
Required

true

  • selectSingle
  • selectCheckBox
  • selectSingleBox
  • selectTree
  • selectMultipleSideBySide

items

items
Type
array

Contains the elements for the selector box. Each item is an array. An item consists of a label and a value.

Example:

items:
  - label: 'The first'
    value: one
  - label: 'The second'
    value: two
  - label: 'The third'
    value: three
Copied!

XLF translation keys for items have the following convention:

<body>
    <trans-unit id="FIELD_IDENTIFIER.items.one.label">
        <source>Label for item with value one</source>
    </trans-unit>
    <trans-unit id="FIELD_IDENTIFIER.items.two.label">
        <source>Label for item with value two</source>
    </trans-unit>
    <trans-unit id="FIELD_IDENTIFIER.items.VALUE.label">
        <source>Label for item with value VALUE</source>
    </trans-unit>
    <trans-unit id="FIELD_IDENTIFIER.items.label">
        <source>Label for item with empty value</source>
    </trans-unit>
</body>
Copied!

default

default
Type
string

Default value set if a new record is created.

maxitems

maxitems
Type
integer

Maximum number of child items. Defaults to a high value. JavaScript record validation prevents the record from being saved if the limit is not satisfied. If maxitems ist set to greater than 1, multiselect is automatically enabled.

minitems

minitems
Type
integer

Minimum number of child items. Defaults to 0. JavaScript record validation prevents the record from being saved if the limit is not satisfied. The field can be set as required by setting minitems to at least 1.

relationship

relationship
Type
string
Default
oneToMany

The relationship defines the cardinality between the relations. Possible values are oneToMany (default), manyToOne and oneToOne. In case of a [x]toOne relation, the processed field will be filled directly with the record instead of a collection of records. In addition, maxitems will be automatically set to 1. If the renderType is set to selectSingle, a relationship manyToOne is automatically inferred.

dbFieldLength

dbFieldLength
Type
int
Default
255

This option can be used to set an alternative size for the database varchar column. The default size is 255.

allowedCustomProperties

allowedCustomProperties
Type
array
Default
["itemsProcConfig"]

Sometimes it is needed to provide custom configuration for the itemsProcFunc functionality. These extra properties need to be explicitly allowed via this option. This option receives an array of those strings. By default, the custom option itemsProcConfig is allowed.

Example

Minimal

Select single:

name: example/select
fields:
  - identifier: select
    type: Select
    renderType: selectSingle
    items:
      - label: 'The first'
        value: one
      - label: 'The second'
        value: two
Copied!

Select multiple:

name: example/select
fields:
  - identifier: select_side_by_side
    type: Select
    renderType: selectMultipleSideBySide
    items:
      - label: 'The first'
        value: one
      - label: 'The second'
        value: two
Copied!

Advanced / use case

Select single:

name: example/select
fields:
  - identifier: select
    type: Select
    renderType: selectSingle
    default: 'one'
    items:
      - label: 'The first'
        value: one
      - label: 'The second'
        value: two
      - label: 'The third'
        value: three
    foreign_table: pages
    foreign_table_where: 'AND {#pages}.{#pid} = 123 ORDER BY uid'
Copied!

Select multiple:

name: example/select
fields:
  - identifier: select_side_by_side
    type: Select
    renderType: selectMultipleSideBySide
    default: 'one'
    minitems: 1
    maxitems: 3
    items:
      - label: 'The first'
        value: one
      - label: 'The second'
        value: two
      - label: 'The third'
        value: three
    foreign_table: pages
    foreign_table_where: 'AND {#pages}.{#pid} = 123 ORDER BY uid'
Copied!

Select tree:

name: example/select
fields:
  - identifier: select_tree
    type: Select
    renderType: selectTree
    size: 5
    foreign_table: 'pages'
    foreign_table_where: 'ORDER BY pages.uid'
    treeConfig:
      parentField: pid
Copied!

Select with icons:

name: example/select
fields:
  - identifier: select_icons
    type: Select
    renderType: selectSingle
    fieldWizard:
      selectIcons:
        disabled: false
    default: 'image-left'
    items:
      - label: 'Image beside text (left)'
        value: image-left
        icon: content-beside-text-img-left
      - label: 'Image beside text (right)'
        value: image-right
        icon: content-beside-text-img-right
      - label: 'Image above text (center)'
        value: image-above
        icon: content-beside-text-img-above-cent
Copied!

SelectNumber

The SelectNumber type generates a simple select field, which only allows numbers / integers.

Settings

Name Type Default Required
integer
array

default

default
Type
integer

Default value set if a new record is created.

items

items
Type
array

Contains the elements for the selector box. Each item is an array. An item consists of a label and a value.

Example:

items:
  - label: 'The first'
    value: 1
  - label: 'The second'
    value: 2
  - label: 'The third'
    value: 3
Copied!

XLF translation keys for items have the following convention:

<body>
    <trans-unit id="FIELD_IDENTIFIER.items.1.label">
        <source>Label for item with value one</source>
    </trans-unit>
    <trans-unit id="FIELD_IDENTIFIER.items.2.label">
        <source>Label for item with value two</source>
    </trans-unit>
    <trans-unit id="FIELD_IDENTIFIER.items.VALUE.label">
        <source>Label for item with value VALUE</source>
    </trans-unit>
</body>
Copied!

Example

Minimal

name: example/select-number
fields:
  - identifier: select_number
    type: SelectNumber
    items:
      - label: 'The first'
        value: 1
      - label: 'The second'
        value: 2
Copied!

Advanced / use case

Select with icons:

name: example/select-number
fields:
 - identifier: select_number_icons
   type: SelectNumber
   fieldWizard:
     selectIcons:
       disabled: false
   default: 2
   items:
     - label: 'Image beside text (left)'
       value: 1
       icon: content-beside-text-img-left
     - label: 'Image beside text (right)'
       value: 2
       icon: content-beside-text-img-right
     - label: 'Image above text (center)'
       value: 3
       icon: content-beside-text-img-above-cent
Copied!

Slug

The Slug type generates a slug field, which generates a unique string for the record.

Settings

Name Type Default Required
string
array

eval

eval
Type
string

unique, uniqueInSite or uniqueInPid.

generatorOptions

generatorOptions
Type
array

Options related to the generation of the slug. Keys:

fields (array)
An array of fields to use for the slug generation. Adding multiple fields to the simple array results in a concatenation. In order to have fallback fields, a nested array must be used.

Example:

generatorOptions:
  fields:
    - header
Copied!

Examples

Minimal

name: example/slug
fields:
  - identifier: slug
    type: Slug
    eval: unique
    generatorOptions:
      fields:
        - header
Copied!

Advanced / use case

name: example/slug
fields:
  - identifier: slug
    type: Slug
    eval: unique
    generatorOptions:
      fields:
        -
          - header
          - fallbackField
        - date
Copied!

Tab

The Tab field can be used to create a new tab in the editor interface. It needs an unique identifier and can be placed between any two fields. Note: Prohibited inside Palettes.

Pre-defined tabs as for example the TYPO3/Appearance tab for Content Elements are available as Basics.

Labels

XLF translation keys for Tabs have the following convention:

<body>
    <trans-unit id="tabs.TAB_IDENTIFIER">
        <source>Label for Tab</source>
    </trans-unit>
    <trans-unit id="COLLECTION_IDENTIFIER.tabs.TAB_IDENTIFIER">
        <source>Label for Tab in Collection</source>
    </trans-unit>
    <trans-unit id="COLLECTION_IDENTIFIER1.COLLECTION_IDENTIFIER2.tabs.TAB_IDENTIFIER">
        <source>Label for Tab in nested Collection</source>
    </trans-unit>
</body>
Copied!

Examples

Minimal

name: example/tab
fields:
  - identifier: text
    type: Text
  - identifier: tab_1
    type: Tab
  - identifier: text2
    type: Textarea
Copied!

For in-depth information about tabs refer to the TCA documentation.

Text

The Text type generates a simple input field, possibly with additional features applied.

Settings

Name Type Default Required
string
integer
integer
string
boolean false
integer
array

default

default
Type
string

Default value set if a new record is created.

max

max
Type
integer

Value for the maxlength attribute of the <input> field. Javascript prevents adding more than the given number of characters.

min

min
Type
integer

Value for the minlength attribute of the <input> field. Javascript prevents adding less than the given number of characters. Note: Empty values are still allowed. Use in combination with required if this should be a non-empty value.

placeholder

placeholder
Type
string

Placeholder text for the field.

required

required
Type
boolean
Default
false

If set, the field becomes mandatory.

size

size
Type
integer

Abstract value for the width of the <input> field.

valuePicker

valuePicker
Type
array

Renders a select box with static values next to the input field. When a value is selected in the box, the value is transferred to the field. Keys:

items (array)
An array with selectable items. Each item is an array with the first being the label in the select drop-down (LLL reference possible) and the second being the value transferred to the input field.

Example:

valuePicker:
  items:
    - [ 'Want to join our team? Take the initiative!', 'Job offer general' ]
    - [ 'We are looking for ...', 'Job offer specific' ]
Copied!

Examples

Minimal

name: example/text
fields:
  - identifier: text
    type: Text
Copied!

Advanced / use case

name: example/text
fields:
  - identifier: text
    type: Text
    default: 'Default value'
    min: 4
    max: 15
    required: true
Copied!

Textarea

The Textarea type is for multi line text input. A Rich Text Editor can be enabled.

Settings

Name Type Default Required
string
string
integer 5
boolean false
boolean false
string

default

default
Type
string

Default value set if a new record is created.

placeholder

placeholder
Type
string

Placeholder text for the field.

rows

rows
Type
integer
Default
5

Abstract value for the height of the <textarea> field. Max value is 20.

required

required
Type
boolean
Default
false

If set, the field will become mandatory.

enableRichtext

enableRichtext
Type
boolean
Default
false

If set to true, the system renders a Rich Text Editor if that is enabled for the editor (default: yes), and if a suitable editor extension is loaded (default: rte_ckeditor).

If either of these requirements is not met, the system falls back to a <textarea> field.

richtextConfiguration

richtextConfiguration
Type
string

The value is a key in $GLOBALS['TYPO3_CONF_VARS']['RTE']['Presets'] array and specifies the YAML configuration source field used for that RTE field. It does not make sense without having property enableRichtext set to true.

Extension rte_ckeditor registers three presets: default, minimal and full and points to YAML files with configuration details.

Integrators may override for instance the default key to point to an own YAML file which will affect all core backend RTE instances to use that configuration.

If this property is not specified for an RTE field, the system will fall back to the default configuration.

Examples

Minimal

name: example/textarea
fields:
  - identifier: textarea
    type: Textarea
Copied!

Richtext field

name: example/richtext
fields:
  - identifier: textarea
    type: Textarea
    enableRichtext: true
    richtextConfiguration: full
Copied!

Advanced / use case

name: example/textarea
fields:
  - identifier: textarea
    type: Textarea
    default: "Default value"
    placeholder: "Placeholder text"
    required: true
    rows: 15
Copied!

Uuid

The Uuid type is for a text input which contains an uuid value.

Settings

Name Type Default Required
integer 30
integer 4
boolean true

size

size
Type
integer
Default
30

Size for the input field. Min value is 10 and max value is 50.

version

version
Type
integer
Default
4

Version for the uuid. Please have a look at the Symphony Documentation for more information.

enableCopyToClipboard

enableCopyToClipboard
Type
boolean
Default
true

If set to false, the button for copying the uuid into the clipboard will not be rendered.

Examples

Minimal

name: example/uuid
fields:
  - identifier: uuid
    type: Uuid
Copied!

Advanced / use case

name: example/uuid
fields:
  - identifier: uuid
    type: Uuid
    size: 50
    version: 7
    enableCopyToClipboard: false
Copied!

Templating

The following examples are for templating with Fluid. Content Blocks brings some additional features like own variables and ViewHelpers with it.

Accessing variables

Inside your frontend.html or backend-preview.html file you can access the properties of your Content Element as usual by the {data} variable. This variable, however, is special. It has real superpowers!

In contrast to the usual array, we are dealing with an object here. This allows us to magically access our own custom properties very easily. Normally you would access the processed properties. This is done by simply accessing the desired property like {data.header}. The raw properties have to be accessed by {data.rawRecord.some_field}.

All fields with relations are resolved automatically to an array. This includes Collection, Select, Relation, File, Folder, Category and FlexForm fields. There is no need to provide additional DataProcessors for them. Content Blocks applies relation resolving for you (recursively!).

Have a look at this code example to grasp what's possible:

<!-- Any property, which is available in the Record (like normal) -->
{data.title}
{data.uid}
{data.pid}

<!-- Normal access to custom relational properties -->
<f:for each="{data.collection1}" as="item">{item.title}</f:for>

<!-- Recursive access to custom relational properties -->
<f:for each="{data.collection1}" as="item">
    <f:for each="{item.categories}" as="category">
        {category.title}
    </f:for>
</f:for>

<!-- Language related properties -->
{data.languageId}
{data.languageInfo.translationParent}
{data.languageInfo.translationSource}

<!-- The overlaid uid -->
{data.overlaidUid}

<!-- Types are a combination of the table name and the Content Type name. -->
<!-- Example for table "tt_content" and CType "textpic": -->

<!-- "tt_content" (this is basically the table name) -->
{data.mainType}

<!-- "textpic" (this is the CType) -->
{data.recordType}

<!-- "tt_content.textpic" (Combination of mainType and record type, separated by a dot) -->
{data.fullType}

<!-- System related properties -->
{data.systemProperties.deleted}
{data.systemProperties.disabled}
{data.systemProperties.lockedForEditing}
{data.systemProperties.createdAt}
{data.systemProperties.lastUpdatedAt}
{data.systemProperties.publishAt}
{data.systemProperties.publishUntil}
{data.systemProperties.userGroupRestriction}
{data.systemProperties.sorting}
{data.systemProperties.description}

<!-- Computed properties depending on the request context -->
{data.computedProperties.versionedUid}
{data.computedProperties.localizedUid}
{data.computedProperties.requestedOverlayLanguageId}
{data.computedProperties.translationSource} <!-- Only for pages, contains the Page model -->

<!-- Workspace related properties -->
{data.versionInfo.workspaceId}
{data.versionInfo.liveId}
{data.versionInfo.state.name}
{data.versionInfo.state.value}
{data.versionInfo.stageId}

<!-- To access the raw (unprocessed) database record use `rawRecord` -->
{data.rawRecord.some_field}
Copied!

See also: https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/13.2/Feature-103783-RecordTransformationDataProcessor.html#usage-in-fluid-templates

Frontend & backend

Content Blocks allows you to provide a separate template for the frontend and the backend out of the box. The variables are the same for both templates, and while using the asset ViewHelpers, you can also ship JavaScript and CSS as you need. The main goal behind this is, that you can provide a better user experience for the editors. With this feature, there is the possibility to provide nearly the same layout in the frontend and the backend, so the editors easily find the element they want to edit.

The frontend template is located in templates/frontend.html and the backend template in templates/backend-preview.html.

Asset ViewHelpers

Content Blocks provides a new AssetPathViewHelper to access assets from within the current Content Block in the template.

<f:comment><!-- Include the assets/frontend.css stylesheet --></f:comment>
<f:asset.css identifier="myCssIdentifier" href="{cb:assetPath()}/frontend.css"/>

<f:comment><!-- Include the assets/frontend.js script --></f:comment>
<f:asset.script identifier="myJavascriptIdentifier" src="{cb:assetPath()}/frontend.js"/>
Copied!

The information of the current Content Block is stored in {data}. This means if you use an asset ViewHelper in a partial, you have to provide {data} as an argument to that partial. Alternatively, you can set name by hand:

<f:comment><!-- The name of the Content Block is set explicitly --></f:comment>
<f:asset.script identifier="myJavascriptIdentifier" href="{cb:assetPath(name: 'vendor/name')}/frontend.js"/>
Copied!

LanguagePath ViewHelper

Content Blocks provides a LanguagePathViewHelper to retrieve the LLL-path for the labels.xlf file of the current Content Block.

<f:translate key="{cb:languagePath()}:header"/>
Copied!

As described above in the asset ViewHelper, the {data} variable is required to resolve the Content Block automatically. You can also set name by hand:

<f:translate key="{cb:languagePath(name: 'vendor/name')}:header"/>
Copied!

Partials

Partials are a very useful feature of Fluid. You can use them to split up your templates into smaller parts. If you want to use a partial in a Content Block, you can create a subdirectory partials in the templates directory and place your partials there.

This part is automatically added, but you can also extend or overwrite this TypoScript configuration in your sitepackage.

Remember, that you should ship the {data} variable to the partial if you want to make use of automatic detection of the current Content Block.

<f:render partial="Component" arguments="{data: data, foo: 'bar'}"/>
Copied!

See also:

Layouts

Analogous to partials, you can also use layouts. You can create a subdirectory layouts in the templates directory and place your layouts there. The configuration is added automatically, but you can also extend or overwrite the TypoScript configuration in your sitepackage. Afterwards you can use your layouts as usual in Fluid.

Shareable resources

There is the technical possibility to use resources from the whole TYPO3 setup (e.g. translations, scripts, or partials from other extensions), but we do not recommend to do so. Content Blocks are intended to work independent of external resources so they can be easily copy-pasted between projects. Be aware of this downside, when you add dependencies to your Content Block.

Language Key Convention

This feature refers to the labels.xlf file.

The translation keys follow a convention and are registered automatically. This feature is available for the following options (more may come).

It is also possible to translate the items option of Select, Radio and Checkbox fields.

The convention is best explained by examples. Have a look at the example beneath.

Workflow

The recommended workflow is defining the label in the config.yaml first. When you are done, you run the command to auto-generate the labels.xlf file. After that you can remove the labels from the yaml definition. You can also skip the first step and generate the xlf without defining labels first. This will add the identifier as label and you can adjust it afterwards. Either way, it is recommended to maintain a labels.xlf file so you don't mix labels with configuration.

Convention schema

The schema below displays most common conventions. Text in uppercase refers to values defined in identifier.

<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file datatype="plaintext" original="labels.xlf" source-language="en" product-name="example">
        <header/>
        <body>
            <!-- Title and description of the Content Type -->
            <trans-unit id="title">
                <source>This is the Content Type backend title</source>
            </trans-unit>
            <trans-unit id="description">
                <source>This is the Content Type backend description</source>
            </trans-unit>
            <!-- Field labels and descriptions for the backend -->
            <trans-unit id="FIELD_IDENTIFIER.label">
                <source>This is the backend label for FIELD_IDENTIFIER</source>
            </trans-unit>
            <trans-unit id="FIELD_IDENTIFIER.description">
                <source>This is the backend description for FIELD_IDENTIFIER</source>
            </trans-unit>
            <!-- Collections add another nesting level -->
            <trans-unit id="COLLECTION_IDENTIFIER.FIELD_IDENTIFIER.label">
                <source>This is the backend label for FIELD_IDENTIFIER in Collection COLLECTION_IDENTIFIER</source>
            </trans-unit>
            <!-- Palette labels and descriptions -->
            <trans-unit id="palettes.PALETTE_IDENTIFIER.label">
                <source>Label for Palette</source>
            </trans-unit>
            <trans-unit id="palettes.PALETTE_IDENTIFIER.description">
                <source>Description for Palette</source>
            </trans-unit>
            <!-- Palettes inside Collections -->
            <trans-unit id="COLLECTION_IDENTIFIER.palettes.PALETTE_IDENTIFIER.label">
                <source>Label for Palette in Collection</source>
            </trans-unit>
            <trans-unit id="COLLECTION_IDENTIFIER1.COLLECTION_IDENTIFIER2.palettes.PALETTE_IDENTIFIER.label">
                <source>Label for Palette in nested Collection</source>
            </trans-unit>
            <!-- Tab labels -->
            <trans-unit id="tabs.TAB_IDENTIFIER">
                <source>Label for Tab</source>
            </trans-unit>
            <!-- Tab labels inside Collections -->
            <trans-unit id="COLLECTION_IDENTIFIER.tabs.TAB_IDENTIFIER">
                <source>Label for Tab in Collection</source>
            </trans-unit>
            <trans-unit id="COLLECTION_IDENTIFIER1.COLLECTION_IDENTIFIER2.tabs.TAB_IDENTIFIER">
                <source>Label for Tab in nested Collection</source>
            </trans-unit>
        </body>
    </file>
</xliff>
Copied!

Backend Preview

Backend previews allow you to provide a preview for your frontend in the backend. Previews are a very important part when creating custom content types for your editors. They are the only way to help them navigate through the jungle fo elements in the page module.

A preview can be defined by creating a file templates/backend-preview.html inside your Content Block. This file is already created for you, if you use the kickstart command.

Content Elements

By default, TYPO3 comes with a standard preview renderer for Content Elements. However, it is specialized in rendering the preview of Core Content Elements. This means only Core fields like header, subheader or bodytext are considered. Therefore, it is advised to provide an own preview for custom Content Elements.

Changed in version 1.1

Previews for Content Elements now must define the layout Preview and any of the sections Header, Content or Footer.

EXT:my_package/ContentBlocks/ContentElements/my-element/templates/backend-preview.html
<f:layout name="Preview"/>

<f:section name="Header">
    <div>My header</div>
</f:section>

<f:section name="Content">
    <f:asset.css identifier="my-backend-styles" href="{cb:assetPath()}/preview.css"/>
    <div>My content</div>
</f:section>

<f:section name="Footer">
    <div>My footer</div>
</f:section>
Copied!

A Content Element preview consists of three parts: The header, the content area and the footer. By defining the appropriate section it is possible to override the standard preview rendering of TYPO3. If a section is omitted, the fallback rendering from TYPO3 will be used instead. You can also include CSS just like in the frontend with f:asset.css View Helper.

Page Types

Previews for Page Types are displayed at the top of the content area and beneath the page title. Unlike Content Elements, you don't have to define any sections.

Page Type preview in the TYPO3 backend

This is an example of a Page Type preview.

Record Types

Previews for Record Types can only be shown as nested child records of Content Elements in the Page Module like so:

<f:comment>Provide the identifier of the child Collection to render a grid preview</f:comment>
<f:render partial="PageLayout/Grid" arguments="{data: data, identifier: 'tabs_item'}"/>
Copied!

See also:

Basics (Mixins)

Basics, also known as partials or mixins, are used to have a pre-defined set of fields that can be used to better organize your config.yaml file and to reduce redundancy by sharing them between multiple Content Blocks.

There are two different ways of using Basics.

Basics as additional fields

The first way of using Basics is to have them added after the fields array of your Content Block. This is useful if you want to have a set of fields that are always available for your Content Block.

This is an example on how to add the classic Fluid Styled Content Appearance Tab and the additional Links palette.

EXT:your_extension/ContentBlocks/ContentElements/basics/config.yaml
name: example/basics
basics:
    - TYPO3/Appearance
    - TYPO3/Links
Copied!

You can add as many Basics as you need. Note, that all Basics are simply concatenated onto each other. Be careful, not to create an invalid state by gluing incompatible Basics together.

Basics as field type

The second way is to use Basics directly in the fields array. This can be done by using the according Basic identifier and the type Basic.

EXT:your_extension/ContentBlocks/ContentElements/basics/config.yaml
name: example/basics
fields:
    - identifier: TYPO3/Header
      type: Basic
Copied!

Pre-defined Basics

List of the standard Basics shipped with Content Blocks. Feel free to copy them into your own project and adjust them as you need.

  • TYPO3/Header
  • TYPO3/Appearance
  • TYPO3/Links
  • TYPO3/Categories

Define own Basics

You can define your own Basics by placing one or more YAML files into ContentBlocks/Basics. The name of the YAML file can be chosen freely. It is also possible to create sub-folders in order to structure your Basics.

Example on how to create a single Basic:

EXT:your_extension/ContentBlocks/Basics/YourBasic.yaml
identifier: Vendor/YourBasic
fields:
  - identifier: a_basic_field
    type: Text
    label: LLL:EXT:sitepackage/Resources/Private/Language/locallang.xlf:a_basic_field
Copied!

The fields part is exactly the same as in the config.yaml. Here you can define a Tab, a Palette or simply a set of fields.

The most practical way to use Basics is to use pre-defined tabs as the global basics option, so they are always added at the end. The field type Basic is used best as a palette. There you can define a set of fields, which you always need e.g. various header fields.

Nested Basics

It is also possible to nest Basics. So if Basic A refers to Basic B in the fields list, then this will be resolved, too. Be careful to not create an infinite loop by circular or self-references. This will be detected automatically, if a high nesting level is reached.

EXT:your_extension/ContentBlocks/Basics/YourBasic.yaml
identifier: Vendor/YourBasic
fields:
  - identifier: a_basic_field
    type: Text
    label: LLL:EXT:sitepackage/Resources/Private/Language/locallang.xlf:a_basic_field
  - identifier: Vendor/AnotherBasic
    type: Basic
Copied!

Content Elements

For YAML reference refer to this page.

Content Elements are a special Content Type in TYPO3. The basic structure is already defined in the TYPO3 Core. Content Blocks only adds new types to it.

A minimal Content Element looks like this:

EXT:your_extension/ContentBlocks/ContentElements/cta/config.yaml
name: example/cta
fields:
  - identifier: header
    useExistingField: true
Copied!

In case you need the well-known Appearance tab back, you can add pre-defined Basics to your definition:

EXT:your_extension/ContentBlocks/ContentElements/cta/config.yaml
name: example/cta
basics:
    - TYPO3/Appearance
    - TYPO3/Links
fields:
  - identifier: header
    useExistingField: true
Copied!

The Appearance tab will then be added after all your custom fields.

About TYPO3 Content Elements

Defining "Content Elements" in TYPO3 is hard and the learning curve is steep.

Despite the possibility to customize TYPO3 to ones needs, most people used the standard Content Elements shipped with TYPO3 Core. This is, of course, convenient, but has several drawbacks as soon as customizations are needed. One override follows the next, until the Core Content Element has more modifications than the initial implementation. This is where the concept of Content Blocks stepped in. First of all, it makes creating new types a no-brainer, so that the temptation to overrides is weakened. Secondly, for the same reason, it makes standard Content Elements almost obsolete. If you need a specific element, you can always copy/paste it into your project as your own element. Owning the elements means there will be no breaking changes to the Fluid templates.

Related documentation:

File Types

New in version 1.2

For YAML reference refer to this page.

File Types are a special Content Type in TYPO3. They relate to the field type file. Every time you create a new file reference, the type of the child record is automatically determined by the file mime type.

There is a fixed amount of types:

  • text
  • image
  • audio
  • video
  • application

TYPO3 already provides a basic palette of fields, including an image manipulation module to handle crop areas. Now, if you happen to need an additional custom field, you have to completely override the type definition. A minimal example looks like this:

EXT:your_extension/ContentBlocks/FileTypes/image/config.yaml
name: example/file-type-image
typeName: image
prefixFields: false
fields:
  - identifier: image_overlay_palette
    type: Palette
    label: 'LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette'
    fields:
      - identifier: alternative
        useExistingField: true
      - identifier: description
        useExistingField: true
      - type: Linebreak
      - identifier: link
        useExistingField: true
      - identifier: title
        useExistingField: true
      - type: Linebreak
      - identifier: example_custom_field
        type: Text
        label: 'My custom Field'
      - type: Linebreak
      - identifier: crop
        useExistingField: true
Copied!

In this example, we re-define the image file type. For the most part everything is identical to the standard definition. We only added another custom field example_custom_field right before the crop field. Using this method, you have full control over the available fields and their position.

File reference in the TYPO3 backend

Nested Content Elements

It is possible to nest Content Elements within Content Blocks. By default, TYPO3 would render those nested elements within the TYPO3 Page Module in the backend and the frontend output. Content Blocks delivers an API and integration for common setups to prevent this unwanted behaviour.

How to create nested Content Elements

In order to have nested Content Elements, we need to make use of the field type Collection. This field type allows us to have a relation to another table. In our case tt_content.

EXT:my_extension/ContentBlocks/ContentElements/tabs/config.yaml
name: example/tabs
fields:
  - identifier: header
    useExistingField: true
  - identifier: tabs_item
    type: Collection
    minitems: 1
    foreign_table: tt_content
    overrideChildTca:
      columns:
        CType:
          config:
            default: example_text
Copied!

This config creates a field, where you can create new Content Elements within your root Content Element. For better usability the default CType can be overridden with overrideChildTca. Right now, it is not possible to restrict certain CTypes.

Render nested Content Elements in the frontend

There are two ways to render nested Content Elements. The first one is to reuse the basic rendering definition of the child element. This is already pre-rendered in the Fluid variable {data._grids.identifier}. This variable contains all relations which might have a frontend rendering definition defined in TypoScript. Normally, these are only Content Elements.

EXT:my_extension/ContentBlocks/ContentElements/tabs/templates/frontend.html
<f:for each="{data._grids.tabs_item}" as="item" iteration="i">
    <f:comment><!-- {item.data} contains the Content Block data object. --></f:comment>
    <div class="tab-item" data-uid="{item.data.uid}">
        <f:comment><!-- {item.content} contains the rendered html. --></f:comment>
        <f:format.raw>{item.content}</f:format.raw>
    </div>
</f:for>
Copied!

The second method is to define an alternative rendering within your Fluid template. This means you can have a default rendering definition for your Content Element, when used as a root Content Element and an alternative one if used as a child. This method is a lot more flexible, but requires a little bit more work.

EXT:my_extension/ContentBlocks/ContentElements/tabs/templates/frontend.html
<f:for each="{data.tabs_item}" as="item" iteration="i">
    <div class="tab-item" data-uid="{item.uid}">
        <h2>{item.header}</h2>
        <f:format.html>{data.bodytext}</f:format.html>
    </div>
</f:for>
Copied!

Render nested Content Elements in the backend

Similarly to frontend rendering, it's also possible to render nested content in the backend. For this Content Blocks provides ready to use Fluid partials which are able to render backend previews the same way the Core page layout does it.

EXT:my_extension/ContentBlocks/ContentElements/tabs/templates/backend-preview.html
<f:render partial="PageLayout/Grid" arguments="{data: data, identifier: 'tabs_item'}"/>
Copied!

The partial is called PageLayout/Grid and accepts your current Content Block data object as well as the identifier of the Collection field, which you want to render.

The partial renders the grid layout from the Core.

This preview is limited to control buttons like edit, delete and hide. No support for drag and drop or creation of new child elements is given.

When to use nested Content Elements vs. container extensions

Many problems can be solved with either solution. However, as a rule of thumb, as soon as you need dynamic multi-grid components for your page, you are better off using something like EXT:container. For example "Two-Column container", "1-1-2 Container" or "Wrapper Container".

On the other hand, if you create a self-contained element, which should hold child Content Element relations and these child elements are unlikely to be moved elsewhere, you might be better off using simple nested Content Elements. An example could be a "Tab module" or a "Content Carousel".

Concept

The nesting is done via one database field holding a reference to the parent Content Element. The colPos column will always be 0, which is also the reason why it would otherwise be rendered by TYPO3 even though created within another Content Element.

Extensions like EXT:container work completely different as they assign a specific colPos value to the child elements.

Preventing output in frontend

Output in frontend is prevented by extending styles.content.get.where condition. This is done via postUserFunc, which will extract all defined parent reference columns. Those are added to the SQL statement in order to prevent fetching any child elements.

Preventing output in backend

TYPO3 provides an event to alter the SQL query to fetch content for the Page Module in the backend. Content Blocks adds an event listener to apply the same logic as for the frontend.

Page Types

For YAML reference refer to this page.

Page Types are a special Content Type in TYPO3. The basic structure is already defined in the TYPO3 Core. Content Blocks only adds new types to it. A minimal Page Type looks like this:

EXT:your_extension/ContentBlocks/PageTypes/blog/config.yaml
name: example/blog
typeName: 1701284006
fields:
  - identifier: additional_field
    type: Text
Copied!

This will create a new Page Type entry above the page tree, which you can drag and drop as usual. Your custom fields will be added after the nav_title field. SEO fields will be automatically added, if you have the SEO system extension installed.

About TYPO3 doktypes

Page Types (also known as doktypes) do exist since the beginning of time. However, it was always hard to create custom ones and there were no extensions to help with creating them as they exist for Content Elements. Due to this circumstances you might not be familiar with the concept of custom Page Types. Basically, you can create a variation which is different from the standard page type and add special properties to it. These can be used in templates or even custom plugins. This opens up many possibilities and can enhance user experience in the backend.

When to use Page Types

Page Types are best suited for cases where you have a set of pages with common properties like a teaser text, image or date. The best example is a news article page. These most often have required fields which are always displayed at a fixed position, like an author at the bottom of the page. These fields should be included as page properties rather than specifically defined Content Elements. The article itself however, should be composed of various Content Elements. This approach also opens up the possibility to use your Page Types in plugins. See the blog extension which utilises this concept.

Frontend template

There is no default frontend template for Page Types, as this depends heavily on your setup. Typically the site package extension of your site defines the page templates depending on the selected page layout.

Read more about Page Type / template mapping.

Backend preview

Just like for Content Elements, you can define an backend-preview.html file to create a preview of your Page Type. This can be used to preview custom properties and to link directly to them. To make them prettier it is advised to utilise CSS bootstrap classes like card.

<div class="card card-size-medium">
    <div class="card-body">
        <be:link.editRecord uid="{data.uid}" table="{data.mainType}" fields="author">
            Author: {data.author}
        </be:link.editRecord>
    </div>
</div>
Copied!

Icons for states

Page Types can have special states like disabled or hidden in menu. Depending on this state, the icon changes or is overlaid with another icon. As for now, the "hide in menu" and "is site root" states can be supplied via custom icons. Put an icon with the name icon-hide-in-menu.svg and one with icon-root.svg inside your assets folder to use them.

Directory structure of a Content Block
├── assets
│   │── icon.svg
│   │── icon-hide-in-menu.svg
│   └── icon-root.svg
└── config.yaml
Copied!

Processing of page data

In order to have resolved relations also for Page Types, you need to add the ContentBlocksDataProcessor to your data processor list.

page {
    10 = FLUIDTEMPLATE
    10 {
        dataProcessing {
            1 = content-blocks
        }
    }
}
Copied!

Remove entry in Page Tree NewPageDragArea

In some cases you don't want your page type to be selectable in the drag area of the page tree. You can remove it with user tsconfig.

EXT:site_package/Configuration/user.tsconfig
options {
  pageTree {
    doktypesToShowInNewPageDragArea := removeFromList(1701284006)
  }
}
Copied!

Field Prefixing

Content Blocks comes with a built-in prefixing mechanism for your custom fields. Whenever you create a new field, the identifier will be prefixed internally with a combination of the name. This is important in order to avoid collisions with other Content Blocks that use the same identifier for their field. Every new field you create will add a new database column to the table. If you want to avoid creating new database columns, see how you can reuse and share fields between different Content Blocks.

Configure prefixing behavior

By default, prefixing is enabled. In case you don't want prefixing at all, you can either disable it globally with prefixFields or on a per field level with prefixField.

The default prefix type is full. That means the complete name is used as a prefix. All dashes are removed and the slash will be converted to an underscore:

my-vendor/my-element️ => myvendor_myelement
Copied!

An alternative to the full prefix is the vendor prefix. This option can be set in prefixType. This does also work on a per field level. By doing so, only the vendor part of name is used as a prefix. This is especially useful if you want all your fields to have the same prefix. In case you just want to have a static prefix, which differs from your vendor, you can set a fixed vendor prefix with vendorPrefix.

Examples

# This will prefix all your fields with "myvendor_myelement"
name: my-vendor/my-element
prefixFields: true
prefixType: full
Copied!
# This will disable prefixing altogether
name: my-vendor/my-element
prefixFields: false
Copied!
# This will prefix all your fields with "myvendor"
name: my-vendor/my-element
prefixFields: true
prefixType: vendor
Copied!
# This will prefix all your fields with "tx_foo"
name: my-vendor/my-element
prefixFields: true
prefixType: vendor
vendorPrefix: tx_foo
Copied!
# This will disable prefixing only for the field "my_field"
name: my-vendor/my-element
prefixFields: true
prefixType: full
fields:
    - identifier: my_field
      type: Text
      prefixField: false
Copied!

Record Types

Record Types are generic Content Types in TYPO3. Basically everything, which is not a Content Element or Page Type. Adding custom records requires you to define a table name. A minimal example looks like this:

EXT:your_extension/ContentBlocks/RecordTypes/my-record-type/config.yaml
name: example/my-record-type
table: tx_vendor_my_record_type
labelField: title
fields:
  - identifier: title
    type: Text
Copied!

This example will create a new table tx_vendor_my_record_type. Usually Record Types are added in folders, so you first have to create a new folder in the page tree. After that, you can switch to the List module and click "Create new record". There you will find your newly created Record Type.

It is also possible to allow creation of Record Types in normal pages. For that you have to enable ignorePageTypeRestriction:

EXT:your_extension/ContentBlocks/RecordTypes/my-record-type/config.yaml
name: example/my-record-type
table: my_record_type
labelField: title
security:
  ignorePageTypeRestriction: true
fields:
  - identifier: title
    type: Text
Copied!

Multi Type Record

In order to create multiple types for a single Record Type, it is required to define a typeField field and a typeName. The type field will be created automatically and added as the very first field in your editing interface as a select dropdown. The different types will be also added automatically to the list:

EXT:your_extension/ContentBlocks/RecordTypes/diver/config.yaml
name: example/diver
table: person
typeField: type
typeName: diver
fields:
  - identifier: title
    type: Text
Copied!
EXT:your_extension/ContentBlocks/RecordTypes/instructor/config.yaml
name: example/instructor
table: person
typeField: type
typeName: instructor
fields:
  - identifier: title
    type: Text
Copied!

Avoiding redundancy

Whenever you have multiple types for a table, you run into the problem on where to define the global capability options like languageAware, workspaceAware or labelField to name a few. Normally it's recommended to keep this configuration in sync between all Content Blocks. The reason behind this is, if you remove one type, the other Content Block still works independently. However, this can cause heavy redundancy. Especially if you are disabling a lot of options. The solution is to declare one of the types as the "default" type. This is done simply by giving it the highest priority. This ensures this Content Block is loaded first and sets the configuration for all other types.

EXT:your_extension/ContentBlocks/RecordTypes/default/config.yaml
name: example/default
table: my_record
priority: 999
typeField: type
typeName: default
languageAware: false
workspaceAware: false
labelField: title
fields:
  - identifier: title
    type: Text
Copied!
EXT:your_extension/ContentBlocks/RecordTypes/special/config.yaml
name: example/special
table: my_record
typeName: special
fields:
  - identifier: title
    type: Text
Copied!

Use as child table in Collection

It is possible to create a new Record Type, which is used as a child table inside a Collection. This is simply done by defining foreign_table inside the Collection. This concept also works for type Select (foreign_table) and type Relation (allowed). The only difference is that you reference existing records instead of creating new ones.

name: example/slide
table: my_slide
labelField: title
fields:
  - identifier: title
    type: Text
  - identifier: image
    type: File
Copied!
name: example/collection
fields:
  - identifier: slides
    type: Collection
    foreign_table: my_slide
    shareAcrossTables: true
    shareAcrossFields: true
Copied!

Adding new types to existing tables

Content Blocks allows to extend existing table definitions with custom types. For this to work, the table in question needs to define type field. Create a new Record Type and define the table, which you want to extend. In addition, you can set a fixed typeName if you wish. In this example we extend the News records with a custom type:

EXT:your_extension/ContentBlocks/RecordTypes/my-news-record/config.yaml
name: example/my-news-record
table: tx_news_domain_model_news
typeName: my_news_record
fields:
  - identifier: title
    useExistingField: true
Copied!

The News record is extended with a custom type.

Content Blocks will automatically create the necessary system field tabs and palettes based on the TCA definition. You just have to define your desired custom fields.

Keep in mind that you probably need to include various required fields like path_segment, datetime or bodytext for your News record to work properly within the extension. This is not a complete guide on how to make custom records work for third-party extbase extensions. Refer to the documentation of those extensions for additional steps. This replaces every step which includes TCA overrides.

With this method it is not possible to override the capabilities of the existing table. This includes options like languageAware, workspaceAware or labelField to name a few.

Reuse Existing Fields

It's possible to reuse already existing fields by using the useExistingField flag. By doing so, you can extend existing fields with your own properties on a per element level.

Reusing base fields

Base fields are fields, which are defined by extensions in Configuration/TCA/table_name.php. They serve as a basis and can be reused in different Content Types. For Content Elements it's highly recommended to reuse the header field this way, because it is used for the title on different places in the backend.

name: example/block
fields:
  - identifier: header
    useExistingField: true
Copied!

Reusing custom fields

Custom fields are fields, which are defined by extensions in Configuration/TCA/Overrides/*.php. They extend the basic set of fields. These can also be reused in Content Blocks, but you have to define the type explicitly in contrast to base fields. For this, you have to know the type mapping from TCA type to Content Blocks type.

name: example/block
fields:
  - identifier: my_custom_field
    type: Text
    useExistingField: true
Copied!

Reusing Content Block fields

Reusing fields between different Content Blocks is only possible, if the option prefixField(s) is turned off. Inside the same project with same vendor names you can also set prefixType to vendor. As soon as the identifier is the same, the field will only be generated once. Be careful to define the same type for the field. Settings can be overridden on a per element basis. Here it is not needed to define useExistingField.

name: example/block1
prefixFields: false # prefixing disabled
fields:
  - identifier: my_custom_field # same identifier
    type: Text # same type
    required: true # different settings
Copied!
name: example/block2
prefixFields: false # prefixing disabled
fields:
  - identifier: my_custom_field # same identifier
    type: Text # same type
    max: 10 # different settings
Copied!

Reusing Select fields

By default, it is not possible to reuse type Select, Radio or Checkbox fields with different items per element. The items have to be the same for every element by, for example, putting them into a Basic. It is still possible to add/remove items with Page TSconfig.

There is one exception to this rule. When used in conjunction with useExistingField the items can be set per element. For this, the field has to be defined in base TCA with no items set. This is mostly for backwards compatibility reasons.

Best practice

It's recommended to use existing fields whenever possible instead of creating new ones. This also avoids the risk of the "Row size too large" problem that can arise when a database table becomes too large and difficult to manage.

It's also important to consider which existing fields are appropriate to reuse (the extension makes no restrictions here or carries out checks). It is generally not recommended to reuse fields such as uid, pid or sorting as they have a specific use and should not be misused due to the potential for negative side effects. Below we have listed the fields from the table tt_content that are eligible for reuse:

  • header
  • header_layout
  • header_position
  • header_link
  • subheader
  • bodytext
  • date
  • image
  • categories
  • pages
  • records

For example, if you want to use the existing column bodytext, header or image you can do one of the following:

name: vendor/content-block-name
fields:
    - identifier: header
      useExistingField: true
    - identifier: bodytext
      useExistingField: true
      enableRichtext: true
    - identifier: image
      useExistingField: true
Copied!

For page types, you may reuse the media field, which is commonly known from the "Resources" tab. It is not included by default, but can be used as needed.

The full list:

  • media
  • categories
  • layout
  • author
  • author_email
  • newUntil
  • lastUpdated

There is an exception for items when used in conjunction with useExistingField for backwards compatibility reasons.

Type Overrides

New in version 1.2

Type Overrides are a feature available for the field type Collection and File. With them it is possible to override a type definition of an existing type when used in the context of a Collection.

Internally the TCA option overrideChildTca is used here to override the showitem string for the specified type.

This feature can be used for different purposes:

  • Reorder fields of the child record
  • Remove fields from the child record
  • Add completely new fields to the child record
  • Override options like label, description, renderType etc.

For either of those purposes you need to re-define the fields definition for the specified type.

Example 1: Override Multi Type Record

name: friendsoftypo3/example
table: tx_friendsoftypo3_example
prefixFields: false
labelField: title
fields:
  -
    identifier: title
    type: Text
    label: Title
  - identifier: collection_override
    type: Collection
    foreign_table: tx_hov_domain_model_record1
    overrideType:
      record1:
        - identifier: type
          type: Select
          useExistingField: true
        - identifier: title
          type: Text
          useExistingField: true
        - identifier: custom_field
          type: Text
Copied!

Here, the type record1 is overridden in the context of the Collection field collection_override. A type override is created with the option overrideType and then with the typeName as the next key. From there you define the usual configuration like in fields.

Example 2: Override Single Type Record

name: friendsoftypo3/example
table: tx_friendsoftypo3_example
prefixFields: false
labelField: title
fields:
  - identifier: title
    type: Text
    label: Title
  - identifier: collection_override
    type: Collection
    foreign_table: tx_hov_domain_model_record
    overrideType:
      - identifier: title
        type: Text
        useExistingField: true
      - identifier: custom_field
        type: Text
Copied!

This is the same as for multi-type records. The only difference is that you can omit the type key. Internally, the typeName 1 is used.

Example 3: Override File Types

name: friendsoftypo3/example
table: tx_friendsoftypo3_example
prefixFields: false
labelField: title
fields:
  - identifier: title
    type: Text
    label: Title
  - identifier: file_override
    type: File
    overrideType:
      image:
        - identifier: image_overlay_palette
          type: Palette
          label: 'LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette'
          fields:
            - identifier: alternative
              useExistingField: true
            - identifier: description
              useExistingField: true
            - type: Linebreak
            - identifier: link
              useExistingField: true
            - identifier: title
              useExistingField: true
            - type: Linebreak
            - identifier: example_custom_field
              type: Text
              label: 'My custom Field'
            - type: Linebreak
            - identifier: crop
              useExistingField: true
Copied!

Lastly, type overrides can be used to re-define file definitions. In this example the file type image is overridden. File Types are usually structured in a palette. This is why there is a type Palette as the first field.

Adding new groups

You need to register the group via PHP API. This will add a new group both for the "NewContentElementWizard" and the record type selector box. It works the same for Page Types and Record Types. Adjust the table and typeField accordingly.

EXT:my_package/Configuration/TCA/Overrides/tt_content.php
<?php

\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItemGroup(
    'tt_content', // table
    'CType', // typeField
    'my_group', // group
    'My group label or LLL:EXT reference', // label
    'before:default', // position
);
Copied!

Create Extbase Plugins

This guide demonstrates how to create Extbase plugins using Content Blocks.

Create a new Content Block

EXT:site_package/ContentBlocks/ContentElements/artists/config.yaml
name: vendor/artists-list
group: plugins
Copied!
EXT:site_package/ContentBlocks/ContentElements/artists/templates/frontend.html
<f:cObject typoscriptObjectPath="{data.mainType}.{data.recordType}.20" table="{data.mainType}" data="{data}"/>
Copied!

Add TypoScript

For extensionName and pluginName use the names as configured in the next step.

EXT:site_package/Configuration/TypoScript/setup.typoscript
tt_content.vendor_artistslist.20 = EXTBASEPLUGIN
tt_content.vendor_artistslist.20 {
    extensionName = MyExtension
    pluginName = MyPlugin
    # If your controller returns a HTML response, set the template path like this:
    view {
        templateRootPaths.0 = EXT:site_package/ContentBlocks/ContentElements/artists/templates
    }
}
Copied!

Register Controller Actions

EXT:site_package/ext_localconf.php
ExtensionUtility::registerControllerActions(
    'MyExtension',
    'MyPlugin',
    [
        ArtistController::class => ['list']
    ],
    [
        ArtistController::class => []
    ]
);
Copied!

Example Controller

This controller simply passes the data from your Content Block to the Fluid template.

EXT:site_package/Classes/Controller/ArtistController.php
<?php

declare(strict_types=1);

namespace Vendor\MyVendor\Controller;

use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;

class ArtistController extends ActionController
{
    public function listAction(): ResponseInterface
    {
        /** @var ContentObjectRenderer $contentObject */
        $contentObject = $this->request->getAttribute('currentContentObject');
        $dataFromTypoScript = $contentObject->data;

        $this->view->assign('data', $dataFromTypoScript);

        return $this->htmlResponse();
    }
}
Copied!

See also:

Extend TCA

Content Blocks generates a lot of boilerplate TCA (Table Configuration Array) for you. Usually you don't need to write own TCA, but in some cases, where you want to override the TCA from Content Blocks, you can do it with own TCA overrides.

How to override Content Blocks TCA

Finding the correct identifier

In order to find out the internal identifier, which is used in TCA you need to know how prefixing works in Content Blocks. You can also simply inspect the TCA in the Configuration module of the typo3/cms-lowlevel extension.

Fields in tt_content

It works exactly like overriding core TCA.

Example:

EXT:sitepackage/Configuration/TCA/Overrides/tt_content.php
$GLOBALS['TCA']['tt_content']['columns']['myvendor_mycontentblock_header']['config']['some_option'] = 'some_value';
$GLOBALS['TCA']['tt_content']['types']['myvendor_mycontentblock']['some_option'] = 'some_value';
Copied!

Fields in custom tables / record types

As soon as you create a Collection field, Content Blocks creates a new custom table. Therefore you need to change the key to the table's name. Extend the TCA in Configuration/TCA/Overrides/myvendor_mycontentblock_mycollection.php. For record types you already defined a tableName, so use this as the key.

Example:

EXT:sitepackage/Configuration/TCA/Overrides/myvendor_mycontentblock_mycollection.php
$GLOBALS['TCA']['myvendor_mycontentblock_mycollection']['columns']['your_field']['config']['some_option'] = 'some_value';
Copied!
EXT:sitepackage/Configuration/TCA/Overrides/my_record_type_table.php
$GLOBALS['TCA']['my_record_type_table']['columns']['your_field']['config']['some_option'] = 'some_value';
Copied!

Extend TypoScript

Content Blocks generate a FLUIDTEMPLATE TypoScript object for each Content Element. This TypoScript can be extended or overridden as needed.

How to extend or override Content Blocks TypoScript

The basis for each Content Element

Every Content Element which is generated by Content Blocks inherits this TypoScript:

lib.contentBlock = FLUIDTEMPLATE
lib.contentBlock {
    dataProcessing {
        10 = content-blocks
    }
}
Copied!

Finding the right TypoScript object

In the TypoScript backend module in the submodule "Active TypoScript", formerly known as "TypoScript Object Browser", the Content Elements created by Content Blocks can be found as usual in the section tt_content.

The name corresponds to the typeName defined in config.yaml.

Example:

tt_content {
    myvendor_mycontentblockname {
        file = EXT:site_package/ContentBlocks/ContentElements/content-element-name/templates/frontend.html
        layoutRootPaths {
            20 = EXT:site_package/ContentBlocks/ContentElements/content-element-name/templates/layouts/
        }
        partialRootPaths {
            20 = EXT:site_package/ContentBlocks/ContentElements/content-element-name/templates/partials/
        }
    }
}
Copied!

This TypoScript object has all the properties that FLUIDTEMPLATE offers.

Extend every Content Block

EXT:site_package/Configuration/TypoScript/setup.typoscript
lib.contentBlock {
    settings {
        defaultHeaderType = {$styles.content.defaultHeaderType}
    }
    variables {
        demo = TEXT
        demo {
            value = some text
        }
    }
    dataProcessing {
        200 = menu
        200 {
          special = rootline
          as = breadcrumb
        }
    }
}
Copied!

Extending a specific Content Block

EXT:site_package/Configuration/TypoScript/setup.typoscript
tt_content.myvendor_mycontentblockname {
    settings {
        defaultHeaderType = {$styles.content.defaultHeaderType}
    }
    variables {
        demo = TEXT
        demo {
            value = some text
        }
    }
    dataProcessing {
        200 = menu
        200 {
          special = rootline
          as = breadcrumb
        }
    }
}
Copied!

See also:

Fluid Styled Content Layouts

This guide describes how you can extend your Content Block templates with the layout from the fluid_styled_content system extension.

You probably know, that fluid styled content (from now on abbreviated FSC) adds special wrappers for its content elements like this:

<div id="c32" class="frame frame-default frame-type-textpic frame-layout-0">
Copied!

These are very helpful, because you can use the anchor id and spacing classes of FSC. In order to wrap all your templates with such a layout you have to provide the FSC layout paths for lib.contentBlock.

Let's have a look at the contents of this TypoScript object:

lib.contentBlock

The contents of lib.contentBlock

As you can see lib.contentBlock is a FLUIDTEMPLATE TypoScript content object. This means you can override it with your own layout and partial root paths.

lib.contentBlock {
    partialRootPaths.0 < lib.contentElement.partialRootPaths.0
    layoutRootPaths.0 < lib.contentElement.layoutRootPaths.0

    settings < lib.contentElement.settings
}
Copied!

By adding the FSC settings, these will also be available in your Content Block templates.

In order to use the standard FSC fields, you should include them as Basics in your config.yaml file. These are already shipped by Content Blocks.

EXT:your_extension/ContentBlocks/ContentElements/fsc-test/config.yaml
name: vendor/fsc-test
title: FSC Element
basics:
  - TYPO3/Appearance
fields:
  - identifier: TYPO3/Header
    type: Basic
Copied!

Now the setup is complete and all what's left to do is to use the newly available layout in your frontend.html templates.

EXT:your_extension/ContentBlocks/ContentElements/fsc-test/templates/frontend.html
<f:layout/>

<!-- This removes the <header> partial of FSC -->
<f:section name="Header"/>

<f:section name="Main">
    <!-- Your content -->
</f:section>
Copied!

This is of course completely optional and you can just remove this per element if you need to. The additional empty <f:section name="Header"/> is important: This removes the <header> partial which FSC would add to your template. If you need this anyway, you can just remove it.

Installation without Fluid Styled Content

It is (and was) always possible to run a TYPO3 installation without the optional extension fluid_styled_content (fsc). With Content Blocks this is even easier as you can create any Content Element you need really fast. If you need a specific Content Element definition from fsc, you can simply re-create it. However, there are some odd specialties which you will encounter when omitting fsc.

Plugins and contentRenderingTemplates

Plugins from other extensions probably won't work without fluid_styled_content as the rendering definition lib.contentElement is defined there for both CType-based and list_type-based plugins. So you have to define it yourself e.g. in your sitepackage.

EXT:site_package/Configuration/TypoScript/setup.typoscript
# Basic rendering of list-type plugins. Normally defined in fluid_styled_content.
tt_content.list = FLUIDTEMPLATE
tt_content.list {
  template = TEXT
  template.value = <f:cObject typoscriptObjectPath="tt_content.list.20.{data.list_type}" table="tt_content" data="{data}"/>
}

# Specific rendering for CType plugins. Normally defined in fluid_styled_content.
# This needs to be added for every registered plugin separately.
tt_content.plugin_name = FLUIDTEMPLATE
tt_content.plugin_name {
  template = TEXT
  template.value = <f:cObject typoscriptObjectPath="tt_content.{data.CType}.20" data="{data}" table="tt_content" />
}
Copied!

The first snippet above defines the default rendering definition for the record type list. This is a generic type for plugins, which further specify their type in the field list_type.

The second snippet defines a rendering definition for a specific CType-based plugin. As these plugins are independent from each other, this needs to be added for every plugin. Tip: If you don't want to repeat this step over and over again it is also possible to define your own lib.contentElement with the FLUIDTEMPLATE cObject:

lib.contentElement = FLUIDTEMPLATE
lib.contentElement {
    templateRootPaths {
        0 = EXT:sitepackage/Resources/Private/Templates/
    }
}
Copied!

Then, add a template with name Generic.html. Add the cObject ViewHelper from the snippet above and it will work automatically. This can also be done for list-type plugin with List.html.

Which method you choose is up to you. The pure TypoScript variant is more robust as the template one, as changes are to be expected in the Core in this area.

contentRenderingTemplates

One thing is still missing: The so called contentRenderingTemplates definition. As the name suggests, it defines where the templates for the content rendering are defined. Without fsc you probably don't have one. And without this definition the TypoScript from above won't work, as it will be loaded too late in the TypoScript parsing process.

EXT:site_package/ext_localconf.php
// Define TypoScript as content rendering template.
// This is normally set in Fluid Styled Content.
$GLOBALS['TYPO3_CONF_VARS']['FE']['contentRenderingTemplates'][] = 'sitepackage/Configuration/TypoScript/';
Copied!

Page Types frontend template

Historically, the page layout (also known as backend layout) is used as the switch for frontend templates and is still considered best practice and was even added as a TYPO3 Core functionality with the PAGEVIEW object. This means you have to define an additional page layout for each new Page Type.

Further interesting read: https://b13.com/blog/simplify-your-typo3-page-configuration

Mapping the Page Type to a template

An alternative, more modern approach is to map the frontend template to the Page Type directly. This makes it possible to have different page layouts per Page Type, but still render the same template. This can heavily reduce the amount of frontend templates which need to be created for each slight layout variation.

In order to make use of this technique, you need to add a CASE cObject with the key set to doktype. This allows us to render a different frontend template depending on the Page Type. This setup expects a Blog.html file inside the Resources/Private/Templates folder in your extension.

page = PAGE
page {
  10 = FLUIDTEMPLATE
  10 {
    templateRootPaths {
      0 = EXT:site_package/Resources/Private/Templates/
    }
    templateName = TEXT
    templateName {
      cObject = CASE
      cObject {
        key.field = doktype

        default = TEXT
        default.value = Default

        1701284006 = TEXT
        1701284006.value = Blog
      }
    }
  }
}
Copied!

Shared Partials

Sometimes you want to reuse Partials from your extension in your Content Block template. This is done by extending the partialRootPaths of the default Content Block FLUIDTEMPLATE definition.

For every Content Block

EXT:site_package/Configuration/TypoScript/setup.typoscript
lib.contentBlock {
    partialRootPaths {
        100 = EXT:site_package/Resources/Private/Partials/ContentElements/
    }
}
Copied!

For a specific Content Block

EXT:site_package/Configuration/TypoScript/setup.typoscript
tt_content.myvendor_mycontentblockname {
    partialRootPaths {
        100 = EXT:my_sitepackage/Resources/Private/Partials/ContentElements/
    }
}
Copied!

For all backend-preview.html templates

Sometimes it is needed to include partials from another source to be used in the preview. For this some Page TsConfig is needed. This can be included in the page.tsconfig file inside your extension, which is automatically loaded. It is also possible to provide additional layout root paths.

EXT:my_extension/Configuration/page.tsconfig
tx_content_blocks {
  view {
    layoutRootPaths {
      10 = EXT:my_extension/Resources/Private/Layouts/
    }
    partialRootPaths {
      10 = EXT:my_extension/Resources/Private/Partials/
    }
  }
}
Copied!

Data Processing

Content Blocks is based on the new Record API in TYPO3 v13. As soon as a Record is created and enriched, it is immutable. This means the standard way of data processing via custom DataProcessors will not work anymore, if it was based on manipulating the $processedData['data'] array.

The new recommended way adding custom logic via PHP is to use the new PSR-14 RecordCreationEvent. This event has the advantage that it is always triggered as soon as a Record is created, independent of frontend or backend context or even in scenarios where the Record is part of a nested structure.

Example

The event listener class, using the PHP attribute #[AsEventListener] for registration, creates a Coordinates object based on the field value of the coordinates field for the custom maps content type.

final class RecordCreationEventListener
{
    #[AsEventListener]
    public function __invoke(\TYPO3\CMS\Core\Domain\Event\RecordCreationEvent $event): void
    {
        $rawRecord = $event->getRawRecord();

        if ($rawRecord->getMainType() === 'tt_content' && $rawRecord->getRecordType() === 'maps' && $event->hasProperty('coordinates')) {
            $event->setProperty(
                'coordinates',
                new Coordinates($event->getProperty('coordinates'))
            );
        }
    }
}
Copied!

Extending Field Types

Content Blocks already comes with pre-defined Field Types. You should utilize them as much as possible. But in case you need special types which cannot be covered from the basic ones, it is possible to extend these with your own types.

When to add new field types

You can add own field types whenever you have fields with a distinct set of configuration options set. These options can be set as default in your type so you gain a semantic naming for this field. For example type Money could be the core TCA type number with format set to decimal as default value.

Another use case is having a custom made TCA renderType, which is not covered by existing field types. This could be e.g. TCA type user with a custom renderType. This way it is possible to use the renderType as a first-class type in Content Blocks.

Adding a new Field Type

To add a new Field Type it is required to implement the \TYPO3\CMS\ContentBlocks\FieldType\FieldTypeInterface. Have a look at the Core implementations to get a feeling on how to implement them. You may extend \TYPO3\CMS\ContentBlocks\FieldType\AbstractFieldType for easier usage. The registration itself happens through the PHP attribute \TYPO3\CMS\ContentBlocks\FieldType\FieldType, which expects the name, tcaType and searchable arguments.

interface FieldTypeInterface
{
    public function getName(): string;
    public function getTcaType(): string;
    public function isSearchable(): bool;
    public function setName(string $name): void;
    public function setTcaType(string $tcaType): void;
    public function setSearchable(bool $searchable): void;
    public function createFromArray(array $settings): FieldTypeInterface;
    public function getTca(): array;
    public function getSql(string $column): string;
}
Copied!

createFromArray

The incoming $settings array is the converted YAML definition. Apply your logic here to instantiate a new type.

getTca

As the name suggests, you generate your TCA config here. This can be done based on the settings provided when createFromArray was called. Of course this can also be a static configuration, if you don't want to provide any settings.

getSql

The SQL definition for your database column. Use $column as the column name. Return empty string to fall back to standard definition. Defined by \TYPO3\CMS\ContentBlocks\FieldType\AbstractFieldType to return empty string for automatic detection. Override this, if you need a specific column definition.

setName/getName

This is the actual type identifier for usage in the YAML type option. It is recommended to use UpperCamelCase, but it's not required. Defined by \TYPO3\CMS\ContentBlocks\FieldType\AbstractFieldType.

setTcaType/getTcaType

The TCA type, the new Content Blocks type is based on. Defined by \TYPO3\CMS\ContentBlocks\FieldType\AbstractFieldType.

setSearchable/isSearchable

Whether the field contents should be searchable in global search. Defined by \TYPO3\CMS\ContentBlocks\FieldType\AbstractFieldType.

Example

Example for a field type "Money".

<?php

declare(strict_types=1);

namespace VENDOR\MyExtension\FieldType;

use TYPO3\CMS\ContentBlocks\FieldType\AbstractFieldType;
use TYPO3\CMS\ContentBlocks\FieldType\FieldType;
use TYPO3\CMS\ContentBlocks\FieldType\WithCommonProperties;

#[FieldType(name: 'Money', tcaType: 'number', searchable: false)]
final class MoneyFieldType extends AbstractFieldType
{
    use WithCommonProperties;

    private float $default = 0.00;
    private bool $required = false;
    private bool $nullable = false;

    public function createFromArray(array $settings): self
    {
        // Clone the service instance, so that state for name, tcaType and searchable is carried over.
        $self = clone $this;
        $self->setCommonProperties($settings);
        $default = $settings['default'] ?? $self->default;
        $self->default = (float)$default;
        $self->required = (bool)($settings['required'] ?? $self->required);
        $self->nullable = (bool)($settings['nullable'] ?? $self->nullable);
        return $self;
    }

    public function getTca(): array
    {
        $tca = $this->toTca();
        $config['type'] = $this->getTcaType();
        if ($this->default !== 0.0) {
            $config['default'] = $this->default;
        }
        if ($this->required) {
            $config['required'] = true;
        }
        $config['format'] = 'decimal';
        $tca['config'] = array_replace($tca['config'] ?? [], $config);
        return $tca;
    }
}
Copied!

Changelog

Every change in the Content Blocks API is documented here.

Table of Contents:

1.0

This is the first stable release for TYPO3 v13. Refer to the TYPO3 v12 migration guide on how to adapt the beta version to be compatible.

1.1

Features

Backend Preview areas

It is now possible to adjust the header and footer for backend previews of Content Elements:

EXT:my_package/ContentBlocks/ContentElements/my-element/templates/backend-preview.html
<f:layout name="Preview"/>

<f:section name="Header">
    <div>My header</div>
</f:section>

<f:section name="Content">
    <f:asset.css identifier="my-backend-styles" href="{cb:assetPath()}/preview.css"/>
    <div>My content</div>
</f:section>

<f:section name="Footer">
    <div>My footer</div>
</f:section>
Copied!

Content Block skeleton

It is now possible to define a "skeleton" for your Content Blocks. To do this create a folder called content-blocks-skeleton in your project root. This folder may contain default templates or assets for one or more Content Types. It is used as a base when creating new types with the make:content-block command. In order to add a skeleton for Content Elements, create a folder called content-element within that directory. Then, the structure is identical to your concrete Content Block as you know it. You may place any files there. They will simply be copied when a new Content Block is created. It is not possible to define language/labels.xlf or config.yaml this way, as they are dynamically generated based on your arguments.

  • content-blocks-skeleton

    • content-element

      • assets

        • icon.svg
      • templates

        • backend-preview.html
        • frontend.html
    • page-type
    • record-type

In case you want to name the skeleton folder differently or place it somewhere else, you can override the default folder by providing the option --skeleton-path with a relative path to your current working directory.

You can use an alternative skeleton path
vendor/bin/typo3 make:content-block --skeleton-path="my-alternative-skeleton-path"
Copied!

Make command defaults

It is now possible to define default options for the make command via a yaml config file. By default, the command looks for a file called content-blocks.yaml in the current working directory. The location and name can be overridden with the --config-path option.

vendor/bin/typo3 make:content-block --config-path="some-folder/my-config.yaml"
Copied!

An example yaml config file contents may look like this:

content-blocks.yaml
vendor: nh
extension: content_blocks_examples
content-type: record-type
skeleton-path: folder1/content-block-skeletons
Copied!

This config sets defaults for vendor, skeleton-path, extension and content-type. These are all possible options right now.

Now, whenever you run this command, these options will be set by default. This does not mean, the questions for these options will be skipped, only that they are the default value, if you just press Enter without any input. They will be visible in brackets at the very right [default value].

Field Type SelectNumber

A new field type SelectNumber is added. This new type allows to have a select field with exclusively integer values. The database column will also have type int, which saves precious row size.

name: example/select-number
fields:
  - identifier: select_number
    type: SelectNumber
    items:
      - label: 'The first'
        value: 1
      - label: 'The second'
        value: 2
Copied!

Deprecations

Backend Preview

Backend previews for Content Elements must use the new layout Preview now. Content Blocks will fall back to the old behavior, if the layout is omitted and will log a deprecation-level log entry.

Before:

<f:asset.css identifier="my-backend-styles" href="{cb:assetPath()}/preview.css"/>
<div>My content</div>
Copied!

After:

<f:layout name="Preview"/>

<f:section name="Content">
    <f:asset.css identifier="my-backend-styles" href="{cb:assetPath()}/preview.css"/>
    <div>My content</div>
</f:section>
Copied!

1.2

Features

File Types

Content Blocks now supports the re-definition of File Types (sys_file_reference). This new Content Type can be defined inside the ContentBlocks/FileTypes folder:

EXT:your_extension/ContentBlocks/FileTypes/image/config.yaml
name: example/file-type-image
typeName: image
prefixFields: false
fields:
  - identifier: image_overlay_palette
    type: Palette
    label: 'LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_file_reference.imageoverlayPalette'
    fields:
      - identifier: alternative
        useExistingField: true
      - identifier: description
        useExistingField: true
      - type: Linebreak
      - identifier: link
        useExistingField: true
      - identifier: title
        useExistingField: true
      - type: Linebreak
      - identifier: example_custom_field
        type: Text
        label: 'My custom Field'
      - type: Linebreak
      - identifier: crop
        useExistingField: true
Copied!

By re-defining the image type like in this example, you can add your custom fields and position them in your desired spot.

Further read:

Type Overrides

Content Blocks now features a way to override types in the context of a Collection with the new field option overrideType. This does also work for the Field Type File.

name: friendsoftypo3/example
table: tx_friendsoftypo3_example
prefixFields: false
labelField: title
fields:
  -
    identifier: title
    type: Text
    label: Title
  - identifier: collection_override
    type: Collection
    foreign_table: tx_hov_domain_model_record1
    overrideType:
      record1:
        - identifier: type
          type: Select
          useExistingField: true
        - identifier: title
          type: Text
          useExistingField: true
        - identifier: custom_field
          type: Text
Copied!

Further read:

1.3

Features

Restrict Collection child types

It is now possible to set specific record types, which should be available for the type selector of Collection items. The most common use case is probably some kind of accordion or tab element, which should only have a few allowed Content Elements as children.

Example

In this example only the Core types text and images are allowed in allowedRecordTypes inside the Collection field.

name: example/tabs
fields:
  - identifier: header
    useExistingField: true
  - identifier: tabs_item
    type: Collection
    minitems: 1
    foreign_table: tt_content
    allowedRecordTypes:
      - text
      - images
Copied!

If you've used Mask before, you know this feature. The Content Blocks implementation is based on the FormDataProvider API in contrast to Mask, which used itemsProcFunc.

Re-definition of Core Page Types

It is now possible to re-define the Core Pages Types of TYPO3:

  • Standard (1)
  • Link to External URL (3)
  • Shortcut (4)
  • Backend User Section (6)
  • Mount Point (7)

This is especially useful if you need custom fields for Core Page Types, but it's not possible to use a custom Page Type like for Shortcuts or External Links. This feature should not be used in third-party extensions, as this would make it impossible for users to define the Page Type themselves.

Examples

Override Page Type "External URL":

name: vendor/external-page
typeName: 3
fields:
  - identifier: additional_field
    type: Text
  - identifier: TYPO3/External
    type: Basic
Copied!

Override Page Type "Shortcut":

name: vendor/shortcut-page
typeName: 4
fields:
  - identifier: additional_field
    type: Text
  - identifier: TYPO3/Shortcut
    type: Basic
Copied!

Default config

The content-blocks.yaml file has been extended to support arbitrary default values for the generated config.yaml file from the make:content-block command.

Example

To use this new feature, create a content-blocks.yaml file in the root directory of your project. Then, add a config key followed by a Content Type identifier. In this case, we set default values for Content Elements, so we use content-element. You can also set default values for page-type, record-type or file-type. Values defined in here override the generated configuration by the command.

config:
  content-element:
    basics:
      - TYPO3/Appearance
      - TYPO3/Links
    group: my_group
    prefixFields: true
    prefixType: vendor
Copied!

Support for PAGEVIEW

The Content Blocks Data Processor is now able to resolve the Page Record for page templates based on PAGEVIEW.

Example

This will add the variable data to your page template which contains the resolved page record. This also works for Page Types not defined by Content Blocks like the default Page Type "1".

page = PAGE
page.10 = PAGEVIEW
page.10 {
  paths.10 = EXT:content_blocks_examples/Resources/Private/Templates
  dataProcessing {
    10 = page-content
    20 = content-blocks
  }
}
Copied!

Core Types resolved to Record Objects

Core Types are now also transformed to Record Objects by the Content Blocks Data Processor. This unifies the experience for users who have both Core defined and Content Blocks defined types in their installation.

Publish Assets Command

The command content-blocks:assets:publish publishes your public Content Block assets into the Resources/Public folder of the host extension. Normally, this is performed automatically every time Content Blocks is compiled. In some deployment scenarios this command could be performed in the CI pipeline to publish assets without the requirement for a database connection.

Migrations

Before Content Blocks, there were many more third-party solutions which enabled easier creation of Content Elements. This collection of guides should show how to migrate these extensions to Content Blocks.

Content Blocks for v12

With the release of Content Blocks 1.0.0 some things changed under the hood, which require migration in your Fluid templates. Also, the folder structure changed as well as usage of ViewHelpers.

New composer name

The composer name has changed.

contentblocks/content-blocks ➡️ friendsoftypo3/content-blocks

Please migrate, as the old package is abandoned.

New folder structure

There is a migration wizard to rename your Content Blocks folders and files to the new structure.

typo3 upgrade:run contentBlocksFolderStructureMigration
Copied!

Ensure the extension with the old Content Block structure is loaded in the system before running this wizard.

New AssetPathViewHelper

We replaced the custom AssetViewHelpers with a new AssetPathViewHelper. Now you can use the Core AssetViewHelpers and only use the custom ViewHelpers to build the path to your asset.

<!-- Before -->
<cb:asset.css identifier="cbAccordionCssBackend" file="EditorPreview.css"/>

<!-- After -->
<f:asset.css identifier="cbAccordionCssBackend" href="{cb:assetPath()}/EditorPreview.css"/>
Copied!

New LanguagePathViewHelper

We replaced the custom TranslateViewHelper with a new LanguagePathViewHelper that is used to build the translation key.

<!-- Before -->
<cb:translate key="readmore"/>

<!-- After -->
<f:translate key="{cb:languagePath()}:readmore"/>
Copied!

Record object

Content Blocks now uses the \TYPO3\CMS\Core\Domain\Record under the hood. This has changed how some record attributes are accessed.

  • {data._raw} ➡️ {data.rawRecord}
  • {data.typeName} ➡️ {data.recordType}
  • {data.tableName} ➡️ {data.mainType}
  • {data.creationDate} ➡️ {data.systemProperties.createdAt}
  • {data.updateDate} ➡️ {data.systemProperties.lastUpdatedAt}
  • {data.localizedUid} ➡️ {data.computedProperties.localizedUid}

Data processing

Content Blocks now uses the \TYPO3\CMS\Frontend\DataProcessing\RecordTransformationProcessor under the hood. This has changed how some fields are transformed.

Folder

The type Folder field will now resolve to a list of \TYPO3\CMS\Core\Resource\Folder objects.

<!-- Before -->
<f:for each="{data.folder}" as="folder">
    <f:for each="{folder}" as="image">
        <f:image image="{item}" />
    </f:for>
</f:for>


<!-- After -->
<f:for each="{data.folder}" as="folder">
    <f:for each="{folder.files}" as="image">
        <f:image image="{item}" />
    </f:for>
</f:for>
Copied!

FlexForm

New: Sub-fields of type FlexForm are now resolved as well.

Groups

The property group now works for both the NewContentElementWizard and for the record selector in the edit view. With this, the way to register groups has changed.

Before:

EXT:my_package/Configuration/page.tsconfig
mod.wizards.newContentElement.wizardItems {
    my_group {
        header = LLL:EXT:my_package/Resources/Private/Language/Backend.xlf:content_group.my_group
        before = common
    }
}
Copied!

After:

EXT:my_package/Configuration/TCA/Overrides/tt_content.php
<?php

\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItemGroup(
    'tt_content', // table
    'CType', // typeField
    'my_group', // group
    'LLL:EXT:my_package/Resources/Private/Language/Backend.xlf:content_group.my_group', // label
    'before:default', // position
);
Copied!

Public assets

The assets folder of your Content Block is now symlinked to the extension's Resources/Public/ContentBlocks/* folder.

Mask

The popular Mask extension is one of the most used extensions in TER. With over one million downloads on packagist it served their users well. Content Blocks is heavily inspired by Mask's architecture. In fact, it could be called the successor of Mask. The combination of Mask and the concept of a Content Block is the result of this extension. Due to this, the migration is quite simple and doesn't even require database migrations.

Automatic migration

The migration creates an exact equivalent of your Mask Elements as Content Blocks. As already mentioned, no database migrations are necessary. The elements will be re-defined in place. The command gathers all assets like icons and templates and puts them into the appropriate folders. The config.yaml file is built based on the mask.json file(s). Templates are simply copied, so they potentially need manual migration.

Install the extension via composer. If you are on classic mode, do the equivalent in the extension manager.

composer req nhovratov/mask-to-content-blocks
Copied!

If you haven't yet installed Content Blocks itself, install it as well:

composer req friendsoftypo3/content-blocks
Copied!

Then run the migration command. This migration will create the Content Blocks into the same extension, where your Mask elements are currently loaded.

vendor/bin/typo3 mask-to-content-blocks:migrate
Copied!

Next, remove Mask and this extension:

composer remove mask/mask nhovratov/mask-to-content-blocks
Copied!

It is still recommended you read the manual migration guide to understand, what the automatic command is doing and what manual steps you have to do afterwards.

Manual migration

Install Content Blocks in parallel to Mask and follow the steps below.

Recreation of a Mask Element

Example Mask slider element

Example Mask slider element

First, let's create a new Content Block for the Mask Element you want to migrate. For this, run the command make:content-block and create a Content Element with a vendor and name of your choice. The name can be the same as the Mask name, but doesn't have to.

Next, you need to override the typeName and disable prefixing. In this example our Mask element has the name slider, so typeName must be set to mask_slider. This is the CType, which Mask generates in the background. If you don't set this option, Content Blocks would create a unique identifier from vendor and name, which you don't want in this case. We disable prefixing, so we can adopt the existing Mask fields.

EXT:site_package/ContentBlocks/ContentElements/slider/config.yaml
name: tx-mask/slider
typeName: mask_slider
prefixFields: false
Copied!

Our Mask element has a repeating slides field. This is a Collection in terms of Content Blocks. To adopt this field, we have to set the identifier prefixed with tx_mask. Also, foreign_field has to be set explicitly to parentid, as this is the column name, which Mask uses for its parent reference field.

All other fields can be re-defined in Content Blocks as usual. Don't forget to prefix all fields with tx_mask, even inside Collections.

EXT:site_package/ContentBlocks/ContentElements/slider/config.yaml
name: tx-mask/slider
typeName: mask_slider
prefixFields: false
fields:
  - identifier: tx_mask_slides
    type: Collection
    foreign_field: parentid
    fields:
      - identifier: tx_mask_header
        type: Textarea
      - identifier: tx_mask_text
        type: Textarea
        enableRichtext: true
      - identifier: tx_mask_image
        type: File
Copied!

Templates

This is the easy part. Simply copy the Mask template into templates/frontend.html and you are done *. That said, if you didn't use any partials or layouts. If so, move these to the according templates/partials and templates/layouts folder in your Content Block.

Icon

In case you've used a custom SVG icon for the Mask Element, you can rename it to icon.svg and move it to the assets folder. For FontAwesome icons you can download the SVG version from the official website.

Labels

Mask doesn't have a translation feature. Labels can be copied from the backend module view, and pasted directly into the labels.xlf file.

Finish

That's mostly it. Now you can purge your Mask element in the backend module view and everything should work as before. Repeat the migration for every Mask Element and you can uninstall the Mask extension in the end and say farewell.

Limitations

There are some very special Mask features, which didn't make the cut to Content Blocks (for good reason).

Page templates fields

Mask has this quirky feature of assigning additional page fields based on TYPO3 backend layouts. There is no such feature in Content Blocks. Use custom page types instead and migrate pages with special backend layouts to the new doktype.

Nested Content

Technically, nested content means the table tt_content has a relation to itself. This can be achieved with Content Blocks as well with the help of Collections. However, the approach is slightly different. Mask sets the fixed value 999 for the colPos field of child elements. All elements with this colPos value are simply hidden from the backend with a hook. Naturally, they won't appear in the frontend, as this colPos is usually not rendered. In contrast, Content Blocks uses the existing parent reference field to systematically determine the status of a child field. This method is far more superior as it doesn't force a random colPos value.

You do the migration as described with Collections above. In addition, you have to add some more config.

EXT:site_package/ContentBlocks/ContentElements/nested-content/config.yaml
name: tx-mask/nested-content
typeName: mask_nested_content
prefixFields: false
fields:
  - identifier: tx_mask_content
    type: Collection
    foreign_table: tt_content
    foreign_field: tx_mask_content_parent_uid
    foreign_table_field: tx_mask_content_tablenames
    foreign_match_fields:
      tx_mask_content_role: tx_mask_content
    allowedRecordTypes:
      - text
      - images
Copied!

The allowed CTypes should be added in allowedRecordTypes.

Disable Content Elements

With Mask, you could disable certain Mask Elements so they won't appear in the Content Element Wizard. This is not possible with Content Blocks. All defined Content Blocks are always available. The migration is to delete those unused elements, or restrict the usage through backend user permissions.

FAQ

Answers to frequently asked questions.

Content Blocks is partly integrated into TYPO3 v13. Since version 1.0 it is based on the new, underlying API from the Core. Most importantly the new Record Transformation API, which was previously implemented in Content Blocks for v12.

Content Blocks is supposed to be the intersection where frontend and backend developers meet. YAML is currently the best suited format for both parties. It allows comments and auto-completion for IDEs with JSON schemas.

This is not possible. The philosophy behind Content Blocks are reusable components, not extendable components. If there is an existing Content Block you want to adapt to your custom needs, simply copy it as a whole. There is no drawback to it. Normally, you don't even want future changes to a Content Block from external parties. By owning the Content Block your code can't break with an update.

If you really need to override a Content Block, you can always fall back to traditional methods like TCA Overrides.

The Content Blocks API does not support PHP files at all. If you want to add some PHP logic to your Content Blocks, you have to use the old common ways.

No, it's not possible. The main rendering engine for TYPO3 is Fluid. If you want to use something else, you are on your own.

Content Blocks generates TYPO3 core code under the hood (TCA, TypoScript and so on), so you have to prepare your headless setup like you have to do with core content elements.

Content Blocks uses core's asset collector, so the assets are not merged together. Since they get registered only if the Content Block is used on the page, each file is included separately.

This is not a specific Content Blocks question. You can add your assets to the build process like you do with any other assets. For example you can add your Content Block CSS to your main CSS file, and remove the CSS registration in the Content Block.

Yes, the Content Types Team is working on a GUI for Content Blocks. The first steps are already done, and we are looking forward to the first beta phase in 2025.

Known Problems

Row size too large

Read this TYPO3 changelog document by @lolli for in depth explanation and more tips.

Field labels displayed as identifier

In some areas in the TYPO3 backend fields created by Content Blocks are displayed with their identifier instead of the localized label. E.g. in the backend user permissions view or the list view / export view. The reason is that you can't define fields centrally in Content Blocks. Multiple Content Blocks can reuse the same field and define alternative labels. If you really need proper labels in these areas, we recommend to use TCA overrides in order to define a default label.

$GLOBALS['TCA']['tt_content']['columns']['my_prefix_my_identifier']['label'] = 'LLL:EXT:my_extension/path/to/locallang.xlf';
Copied!

Core Content Types

Here is an overview of the required and optional languages / places for defining new Content Types the Core way (without Content Blocks). See also: Create a custom content element type. Note that Content Blocks uses these technologies under the hood and you should still learn these to understand how TYPO3 works internally.

SQL

When you need a custom field or a custom table, first of all you have to define a SQL schema. This requires knowledge of MySQL syntax and which column types are suitable for the field. Content Blocks already knows which types are the best for the job and adds them via a PSR-14 Event. For experienced integrators, it is still possible to override the default SQL definitions.

TCA

The Table Configuration Array is a TYPO3 specific PHP structure to define the behavior of SQL columns inside the TYPO3 backend. It is quite powerful and has plenty of options. In order to not limit the capabilities of Content Blocks all options and field types are still available. You will recognize them in the YAML definition. There are some improvements, which required slight naming changes. Content Blocks greatly reduces the amount of code, which needs to be defined, by automatically generating redundant information. E.g. by adding new types to the type selector or placing custom fields to a designated destination. Also icons are registered automatically, if they are placed in a fixed folder.

XML / FlexForm

If one wants to make use of TYPO3 FlexForms, it is required to define an XML file and register it in TCA. Content Blocks streamlines this by using the same syntax for both normal fields and FlexForm fields. There is no need to write TCA inside of XML anymore. Everything is handled in the single YAML definition.

XML / XLF

Translatable labels are implemented by using XLF files in TYPO3. In Content Blocks you still have to write those. However, all fields get a fixed key, which consists of the Content Block name and the field identifier. You only need to remember this standardized schema and they will automatically be used for the backend labels. There is a fallback to the field identifier, if no entry for the field is defined, so that you never encounter fields without a label.

TSConfig

TSConfig is special variant of TypoScript, which allows to modify the backend behavior. It is required to define some TSConfig in order to register Content Elements in the Content Element Wizard. Content Blocks already holds all necessary information for this and adds default TSConfig. If not otherwise defined, new Content Elements are placed in the "common" tab.

TypoScript

TypoScript is the glue between the backend and the frontend. For Content Elements specifically it is required to define a rendering definition, if you intend to display them in the frontend. Content Blocks automatically adds default TypoScript, which registers a Fluid template for the Content Element. In addition, all relations are resolved automatically, so you don't have to define DataProcessors for common use cases like file references or collections.

Fluid

Fluid is the templating engine for TYPO3 and is both used in the backend and the frontend. Content Blocks registers a frontend.html and a backend-preview.html file, which can immediately be used for styling your Content Element. Layouts and partials can be used as usual.

TCA type mapping

Content Blocks redefines all Core TCA types Most of them are the same, but have a capital letter as the first character. Some are slightly different. Here is an overview of the mapping. This is also useful to know, if you want to reuse custom fields.

TCA ContentBlocks
category Category
check Checkbox
inline Collection
color Color
datetime DateTime
email Email
file File
flex FlexForm
folder Folder
language Language
link Link
number Number
passthrough Pass
password Password
radio Radio
group Relation
select Select
slug Slug
input Text
text Textarea
uuid Uuid
json Json

History

The beginning

The content blocks concept is a outcome of the structured content initiative. Since there is a consensus in the community that we appreciate nearly endless possibilities to create a new content element in TYPO3, but the creation of a simple content element is still a very complex task.

There were different ideas and solutions for that issue in TER. The download count proves that there is a need for a solution. So the community aimed for a solution shipped by the core.

So in 2019 the structured content initiative reached out to the community to find a solution for this aim. The initiative analyzed the existing solutions, extensions in the TER like EXT:mask, EXT:dce or EXT:flux and summarized the pros and cons of each solution. After that, there were different ideas and solutions developed and discussed in the community.

With the feedback of the community, the structured content initiative came up with a concept for a new solution called "content blocks", the smallest chunk of information which is needed to create a new content element.

The content blocks concept

The content blocks concept is a new way to create content elements in TYPO3.

The outcome of the research and the discussions in the community is:

  • A content block is a definition in your {extDir}/ContentBlocks/ folder, which will be determined and loaded automatically
  • A content block is not a TYPO3 Extension but a sub-resource, so there is no PHP, TypoScript, SQL nor TSconfig inside
  • The configuration for a new content element is stored in a YAML file, the config.yaml
  • Content blocks are made for frontend developers / TYPO3 beginners as main target group
  • As a common best practice in TYPO3, labels and every other language related stuff is stored in a XLF file. This will be registered and processed by convention
  • The entered name (vendor/name) in the config.yaml file defines the identifier of a content block
  • GUI to create / kickstart a new content block
  • If there are breaking changes, support e.g via UpgradeWizards to migrate easily to the new version
  • Better UX for editors by shipping a template (backend-preview.html) for the backend preview
  • Usage of the AssetCollector: JavaScript and CSS in backend and frontend
  • The content blocks are encapsulated, in a own folder
  • You can move a content block from one project to another project and it will work out of the box
  • Content blocks are a standalone solution, so you can use it without Fluid Styled Content or other bootstrap extensions

What it does

Basically, this extension registers the new content type with the entered vendor/name in the corresponding config.yaml file in TYPO3 and the newContentElementWizard, and translates the YAML-file into TCA and SQL. It registers the labels.xlf and sets the labels and descriptions by the field identifiers, registers the icon and adds the necessary TypoScript.

So it is a abstraction layer to ease up and speed up the work of integrators and frontend developers. But in the end, it outputs the same definitions as a normal TYPO3 content element, which can be overwritten by the integrator.

The first proof of concept

The first proof of concept was created by the structured content initiative and is called contentblocks_reg_api. This extension introduces the new API to TYPO3 v10 and v11. In this first proof of concept, the content blocks are stored in a FlexForm structure. But even if this was just a proof of concept, the community was not happy with storing data as FlexForm. BesideS the API, the POC delivers also a simple GUI to kickstart a new content block. The collections in this state are stored in one single table. The field types at this point were oriented on the Symfony types.

Learnings from the contentblocks_reg_api

Learnings are:

  • The community is not happy with the FlexForm solution
  • The GUI is essential for the usage
  • Overriding of TCA is essential to add custom functions
  • Copy and paste is useful and easy
  • Writing the YAML file is not so hard as expected, but a GUI for editing would be beneficial
  • The identifiers in the labels.xlf are not so easy to work with, would be better to have a GUI for that
  • The GUI / kickstarter in big projects should not be available in production environment

The data storage research

The first proof of concept was stored the structure and data in FlexForm. But despite the hint, that this is just for getting a first impaction, this leads to massive contrary feedback from the community. Meanwhile the structured content initiative did a research and discussed the different possibilities to store the data. The possible solutions were:

  • FlexForm
  • JSON / JSON Blob
  • Database columns
  • EAV (Entity Attribute Value)

The result of the research was, that the best solution is to store the data in the database.

Summary of the decision

FlexForm:

Store data in FlexForm is good for a quick structure, it delivers the possibilities of sections which allows to store a kind of inline data or repeatable elements without the need of a custom table. But the main problem is, that the data is stored in a XML structure, which is not easy to work with. It doesn't provide all of the TCA features and there were some challenges with translations. Furthermore, the community replayed that they are not happy with the FlexForm solution of the proof of concept.

JSON / JSON Blob:

The JSON solution is a good solution for storing the data, but there is a big issue with the that: There is no DataProcessor for JSON in TYPO3 at the moment. So this has to be done by hand and would be a complete new technology for TYPO3. This technology afterwards would have to be maintained and supported by the TYPO3 core team. This would cost a lot of more resources which aren't available at the moment.

Database columns and custom tables:

The database columns will work out of the box, but if there are too many columns, this leads to an error. But it is the most common way to store data in TYPO3 and it is the most stable solution at the moment. To reduce the amount of columns, there is a possibility to reuse a column. So the decision was to use the database columns technique.

EAV (Entity Attribute Value):

The EAV solution is like the JSON solution not implemented in TYPO3 at the moment. So this would be a lot of work to implement this and due to lack of resources, this is not possible at the moment.

Why YAML?

At the time this solution was met, the Form Framework and the routing was introduced a few years ago. Despite the fact that Symfony was turning away from YAML in this time, we stuck to this solution.

Reasons for YAML:

  • YAML is a human readable format
  • YAML is a very common format in the TYPO3 community
  • The core provides a YAML parser
  • YAML can import other YAML files, so complex structures can be split into multiple files
  • YAML is a simple configuration format, without any logical stuff to learn

Why not PHP?

PHP would deliver a lot of possibilities, but it didn't fit to the vision of the content blocks:

  • PHP is not a frontend technique
  • In general, content blocks should be easy to understand and so there should not be any PHP code inside
  • PHP is not a configuration format

Why not JSON?

In fact, JSON is a good solution, but there are some reasons why we decided against JSON:

  • JSON is a less used format in TYPO3
  • JSON is not extendable or can import other files
  • JSON does not support comments

Fusion with EXT:Mask - Nikita Hovratov joined the team

In 2022, Nikita Hovratov from EXT:mask joined the team. He is the main developer of the mask extension and has a lot of experience with the TYPO3 core and the issues which are addressed by the content blocks. We decided to join forces and bring together the best of both worlds.

Developing a Core system extension

As we started to develop the system extension, we had to better adapt to core standards. The content blocks extension is developed completely from scratch in a new way.

Types are changed:

We decided to adapt the types to the new TCA types in TYPO3 v12, so the amount of fields to learn is reduced. This is because the TCA now supports much easier to use types.

Separate the GUI from the core extension:

While developing the system extension, there where several discussions to separate the GUI from the core extensions. The reasons for this are:

  • Mostly, you don't want to have the GUI in the production environment
  • The GUI should not stuck to the release scheduling of the core, so we can add features faster
  • There is the vision, that the GUI should be a website and/or a backend module
  • Platform for sharing content blocks, where you can create and download content blocks

Sitemap