TYPO3 Content Blocks

Extension key

content_blocks

Package name

typo3/cms-content-blocks

Version

0.7

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

Tue, 24 Jun 2025 16:55:12 +0000

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

What is a Content Block?

Install the Content Blocks extension.

Learn about which parts a Content Block consists.

Kickstart a new Content Block.

Reference of all available settings.

Learn to use Content Blocks inside your Fluid template.

Comprehensive guides for specific use cases.

Features targeted to developers specifically.

Table of Contents

Introduction

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

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 basis for field definitions. Content Blocks acts hereby as a generator for more complicated low-level code and makes smart assumptions based on best practice. This significantly reduces redundancies and boilerplate code and enables users to concentrate on their actual work rather than fiddling with the almighty Core API.

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

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.

Directory structure of a Content Block
├── Assets
│   └── Icon.svg
├── Source
│   ├── Language
│   │   └── Labels.xlf
│   ├── EditorPreview.html
│   └── Frontend.html
└── EditorInterface.yaml
Copied!

EditorInterface.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/EditorInterface.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.

Content Blocks reside in the ContentBlocks folder of an extension
├── Classes
├── Configuration
├── ContentBlocks
│   ├── ContentElements
│   │   ├── block1
│   │   └── block2
│   ├── PageTypes
│   │   ├── block3
│   │   └── block4
│   └── RecordTypes
│       ├── block5
│       └── block6
├── Resources
└── composer.json
Copied!

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

Using Composer:

composer require friendsoftypo3/content-blocks:^0.7
Copied!

For installations that don't use Composer, activate the Content Blocks extension in the Extension Manager.

Definition

The minimal viable definition consists of a folder with a YAML file named EditorInterface.yaml inside. All other resources are split into two folders named Assets and Source. These include public resources, translations and templates.

Directory structure of a Content Block
├── Assets
│   └── Icon.svg
├── Source
│   ├── Language
│   │   └── Labels.xlf
│   ├── Partials
│   │   └── Component.html
│   ├── EditorPreview.html
│   └── Frontend.html
└── EditorInterface.yaml
Copied!

EditorInterface.yaml

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

EXT:some_extension/ContentBlocks/ContentElements/content-block-name
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. In composer-mode this folder will be symlinked and published in the public _assets 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.

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).

IconHideInMenu.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.

Source

The Source 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.

EditorPreview.html

This file is only available for Content Elements and Page Types.

The EditorPreview.html can be added to customize the backend preview for your editors. By default, TYPO3 comes with a standard preview renderer. 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. Previews for Page Types are displayed at the top of the content area and beneath the page title.

See also:

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.html" arguments="{_all}"/>
Copied!

See also:

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.

Convention

The translation keys follow a convention and are registered automatically. First of all, title and description are used in various areas for the Content Type. Field labels consist of the identifier and label separated by a dot. Same goes for the optional description.

Collections introduce another nesting level. To translate their fields, the identifiers are simply separated by a dot.

Palettes and Tabs have a special convention as well.

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

Have a look at the example beneath for better understanding.

Workflow

The recommended workflow is defining the label in the EditorInterface.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.

<?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" resname="title">
                <source>This is the Content Type backend title</source>
            </trans-unit>
            <trans-unit id="description" resname="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" resname="FIELD_IDENTIFIER.label">
                <source>This is the backend label for FIELD_IDENTIFIER</source>
            </trans-unit>
            <trans-unit id="FIELD_IDENTIFIER.description" resname="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" resname="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!

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.

Content Blocks reside in the ContentBlocks folder of an extension
├── Classes
├── Configuration
├── ContentBlocks
│   ├── ContentElements
│   │   ├── block1
│   │   └── block2
│   ├── PageTypes
│   │   ├── block3
│   │   └── block4
│   └── RecordTypes
│       ├── block5
│       └── block6
├── Resources
└── composer.json
Copied!

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

Administration

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

content-type

content-type
Required

true

Type

string

content-element, page-type or record-type

vendor

vendor
Required

true

Type

string

Your vendor name. Lowercase, separated by dashes.

name

name
Required

true

Type

string

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

extension

extension
Required

true

Type

string

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

title

title
Required

false

Type

string

The human-readable title for your Content Block.

type-name

type-name
Required

false

Type

string|int

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

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 the newly added database tables or fields, first 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!

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 EditorInterface.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" resname="title">
                <source>My demo title</source>
            </trans-unit>
            <trans-unit id="description" resname="description">
                <source>This is just a demo/demo</source>
            </trans-unit>
            <trans-unit id="header.label" resname="header.label">
                <source>Existing field override</source>
            </trans-unit>
            <trans-unit id="slug.label" resname="slug.label">
                <source>My Slug</source>
            </trans-unit>
            <trans-unit id="slug.description" resname="slug.description">
                <source>My Slug Description</source>
            </trans-unit>
            <trans-unit id="my_collection.label" resname="my_collection.label">
                <source>my_collection</source>
            </trans-unit>
            <trans-unit id="my_collection.text.label" resname="my_collection.text.label">
                <source>text</source>
            </trans-unit>
            <trans-unit id="my_collection.my_collection.label" resname="my_collection.my_collection.label">
                <source>my_collection</source>
            </trans-unit>
            <trans-unit id="my_collection.my_collection.text.label" resname="my_collection.my_collection.text.label">
                <source>text</source>
            </trans-unit>
            <trans-unit id="external_table.label" resname="external_table.label">
                <source>external_table</source>
            </trans-unit>
            <trans-unit id="external_table_2.label" resname="external_table_2.label">
                <source>external_table_2</source>
            </trans-unit>
            <trans-unit id="related_content.label" resname="related_content.label">
                <source>related_content</source>
            </trans-unit>
            <trans-unit id="my-custom-key" resname="my-custom-key">
                <source>My translation</source>
            </trans-unit>
        </body>
    </file>
</xliff>
Copied!

YAML reference

The heart of a Content Block is the EditorInterface.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

name
Required

true

Type

string

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
Required

false

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
Required

false

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. Read more about reusing fields here.

prefixFields: false
Copied!

prefixType

prefixType
Required

false

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!

vendorPrefix

vendorPrefix
Required

false

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!

priority

priority
Required

false

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!

fields

fields
Required

false

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!

Content Elements

Folder: ContentBlocks/ContentElements.

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. The typeName for CType will be generated automatically from the name. Usually you don't need to know how it is called internally. If you do need to know the name, you can inspect it e.g. in the Page TsConfig module.

A minimal Content Element looks like this:

EXT:your_extension/ContentBlocks/ContentElements/cta/EditorInterface.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/EditorInterface.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.

Options

Here you can find all common root options.

description

description
Required

false

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
Required

false

Type

string

Default

common

By default, all new Content Elements are placed in the common tab of the "New Content Element Wizard". You can choose another tab, if you want to group your elements.

group: special
Copied!

The Core defines these standard tabs, which are always available:

  • common
  • menu
  • special
  • forms
  • plugins

typeName

typeName
Required

false

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
Required

false

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!

Page Types

Folder: ContentBlocks/PageTypes.

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/PageType/blog/EditorInterface.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.

Options

Here you can find all common root options.

typeName

typeName
Required

true

Type

integer

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

typeName: 1701284021
Copied!

Record Types

Folder: ContentBlocks/RecordTypes.

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/EditorInterface.yaml
name: example/my-record-type
table: tx_vendor_my_record_type
labelField: title
fields:
  - identifier: title
    type: Text
Copied!

Check out this comprehensive guide on ways to utilize Record Types.

Options

Here you can find all common root options.

table

table
Required

true

Type

string

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

table: tx_vendor_my_custom_table_name
Copied!

labelField

labelField
Required

true

Type

string|array

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
Required

false

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
Required

false

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
Required

false

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
Required

false

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
Required

false

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
Required

false

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
Required

false

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
Required

false

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
Required

false

Type

boolean

Default

true

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

trackCreationDate: false
Copied!

trackUpdateDate

trackUpdateDate
Required

false

Type

boolean

Default

true

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

trackUpdateDate: false
Copied!

sortable

sortable
Required

false

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
Required

false

Type

string|array

Default

true

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
Required

false

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
Required

false

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
Required

false

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
Required

false

Type

boolean

Default

false

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

readOnly: true
Copied!

adminOnly

adminOnly
Required

false

Type

boolean

Default

false

If enabled, only admins can edit the record.

adminOnly: true
Copied!

hideAtCopy

hideAtCopy
Required

false

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
Required

false

Type

string

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

appendLabelAtCopy: append me
Copied!

Field types

The Content Block field types mirror the available TCA types. Some types have been renamed to better reflect the actual usage. For the most part options are identical. There are some additional options, which are not available in TCA to ease the usage.

Common field options

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

identifier

identifier
Required

true

Type

string

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
Required

true

Type

string

The field's type. See Field types.

fields:
    identifier: my_identifier
    type: Text
Copied!

label

label
Required

false

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
Required

false

Type

string

The same as for label above.

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

useExistingField

useExistingField
Required

false

Type

bool

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
Required

false

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!

prefixType

prefixType
Required

false

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!

displayCond

displayCond
Required

false

Type

string|array

Default

''

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
Required

false

Type

string

Default

''

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. Read the main article about Basics here.

Example:

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

Category

type => 'category' // TCA

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

Settings

relationship

relationship
Required

false

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
Required

false

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
Required

false

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
Required

false

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

type => 'check' // TCA

The Checkbox type generates one or more checkbox fields.

Settings

default

default
Required

false

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
Required

false

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
Required

false

Type

string

Default

check

  • checkboxToggle
  • checkboxLabeledToggle

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.

For advanced configuration refer to the TCA documentation.

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

type => 'inline' // TCA

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

labelField

labelField
Required

true

Type

string|array

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
Required

false

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
Required

false

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
Required

true

Type

array

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
Required

false

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
Required

false

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.

appearance.collapseAll

appearance.collapseAll
Required

false

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
Required

false

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
Required

false

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
Required

false

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
Required

false

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
Required

false

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.

For more advanced configuration refer to the TCA documentation

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

type => 'color' // TCA

The Color type provides a simple color picker.

Settings

default

default
Required

false

Type

string

Default

''

Default value set if a new record is created.

required

required
Required

false

Type

boolean

Default

false

If set, the field will become mandatory.

valuePicker

valuePicker
Required

false

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!

For more advanced configuration refer to the TCA documentation.

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

type => 'datetime' // TCA

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

Settings

default

default
Required

false

Type

string

Default

''

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

format

format
Required

false

Type

string

Default

''

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

dbType

dbType
Required

false

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
Required

false

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
Required

false

Type

boolean

Default

false

Disable the display of the age in the backend view.

required

required
Required

false

Type

boolean

Default

false

If set, the field becomes mandatory.

For more advanced configuration refer to the TCA documentation.

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

type => 'email' // TCA

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

default

default
Required

false

Type

string

Default

''

Default value set if a new record is created.

placeholder

placeholder
Required

false

Type

string

Default

''

Placeholder text for the field.

required

required
Required

false

Type

boolean

Default

false

If set, the field becomes mandatory.

For more advanced configuration refer to the TCA documentation.

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

type => 'file' // TCA

The File type generates a field for file relations.

Settings

extendedPalette

extendedPalette
Required

false

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
Required

false

Type

string|array

Default

''

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

maxitems

maxitems
Required

false

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
Required

false

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.

cropVariants

cropVariants
Required

false

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!

For more advanced configuration refer to the TCA documentation.

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!

FlexForm

type => 'flex' // TCA

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
Required

true

Type

array

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

identifier

identifier
Required

true

Type

string

A unique identifier

label

label
Required

false

Type

string

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

description

description
Required

false

Type

string

Define a description.

linkTitle

linkTitle
Required

false

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

identifier

identifier
Required

true

Type

string

A unique identifier

label

label
Required

false

Type

string

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

container

container
Required

true

Type

array

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

type => 'folder' // TCA

The Folder type enables to select one or more folders. Files within these folders will be resolved automatically and are available in Fluid.

Settings

recursive

recursive
Required

false

Type

boolean

Default

false

Files in the selected folder will be resolved recursively.

elementBrowserEntryPoints

elementBrowserEntryPoints
Required

false

Type

array

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

maxitems

maxitems
Required

false

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
Required

false

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.

For more advanced configuration refer to the TCA documentation.

Examples

Minimal

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

Advanced / use case

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

Json

type => 'json' // TCA

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

Settings

cols

cols
Required

false

Type

integer

Default

30

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

rows

rows
Required

false

Type

integer

Default

5

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

required

required
Required

false

Type

boolean

Default

false

If set, the Json textarea needs to be filled.

readOnly

readOnly
Required

false

Type

boolean

Default

false

If set, the Json textarea is read only.

enableCodeEditor

enableCodeEditor
Required

false

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

type => 'language' // TCA

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
Required

false

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

type => 'number' // TCA

The Number only allows integers or decimals as input values.

Settings

format

format
Required

false

Type

string

Default

'integer'

Possible values: integer (default) or decimal.

default

default
Required

false

Type

integer

Default

"0"

Default value set if a new record is created.

range

range
Required

false

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
Required

false

Type

boolean

Default

false

If set, the field becomes mandatory.

slider

slider
Required

false

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!

For more advanced configuration refer to the TCA documentation.

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.

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.

Password

type => 'password' // TCA

The Password type generates a password field.

Settings

hashed

hashed
Required

false

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
Required

false

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
Required

false

Type

string

Placeholder text for the field.

required

required
Required

false

Type

boolean

Default

false

If set, the field becomes mandatory.

size

size
Required

false

Type

integer

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

For more advanced configuration refer to the TCA documentation.

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

type => 'radio' // TCA

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

default

default
Required

false

Type

string|int

Default

''

Default value set if a new record is created.

items

items
Required

true

Type

array

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.

For more advanced configuration refer to the TCA documentation.

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

type => 'group' // TCA

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

Settings

allowed

allowed
Required

true

Type

string (table name, comma-separated)

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
Required

false

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
Required

false

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.

For more advanced configuration refer to the TCA documentation.

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

type => 'select' // TCA

The Select type generates a simple select field.

Settings

renderType

renderType
Required

yes

Type

string

  • selectSingle
  • selectCheckBox
  • selectSingleBox
  • selectTree
  • selectMultipleSideBySide

items

items
Required

false

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
Required

false

Type

string

Default value set if a new record is created.

maxitems

maxitems
Required

false

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
Required

false

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.

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.

For more advanced configuration refer to the TCA documentation.

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'
    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 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!

Slug

type => 'slug' // TCA

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

Settings

eval

eval
Required

false

Type

string

unique, uniqueInSite or uniqueInPid.

generatorOptions

generatorOptions
Required

false

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!

For more advanced configuration refer to the TCA documentation.

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.

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

type => 'input' // TCA

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

Settings

default

default
Required

false

Type

string

Default value set if a new record is created.

max

max
Required

false

Type

integer

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

min

min
Required

false

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
Required

false

Type

string

Placeholder text for the field.

required

required
Required

false

Type

boolean

Default

false

If set, the field becomes mandatory.

size

size
Required

false

Type

integer

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

valuePicker

valuePicker
Required

false

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!

For more advanced configuration refer to the TCA documentation.

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

type => 'text' // TCA

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

Settings

default

default
Required

false

Type

string

Default value set if a new record is created.

placeholder

placeholder
Required

false

Type

string

Placeholder text for the field.

rows

rows
Required

false

Type

integer

Default

5

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

required

required
Required

false

Type

boolean

Default

false

If set, the field will become mandatory.

enableRichtext

enableRichtext
Required

false

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
Required

false

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

type => 'uuid' // TCA

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

Settings

size

size
Required

false

Type

integer

Default

30

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

version

version
Required

false

Type

integer

Default

4

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

enableCopyToClipboard

enableCopyToClipboard
Required

false

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!

Basics (Mixins)

Basics are a concept like partials or mixins. They are used to have a pre-defined set of fields that can be reused and have to be defined only once. It is important to understand that Basics are a very simple "search & replace" kind of mechanic. Once included in your Content Block they act like they were defined there directly. This also means that it is normally required to re-define the label in every Content Block. Hence it is recommended to reference labels with the full LLL:EXT path.

There are two different ways of using it.

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/EditorInterface.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/EditorInterface.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 EditorInterface.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!

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 EditorPreview.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. Let's have a look at the debug output of it:

TYPO3\CMS\ContentBlocks\DataProcessing\ContentBlockData [prototype] [object]
   _raw => [private] array(85 items)
   _processed => [private] array(8 items)
      uid => 24 (integer)
      pid => 1 (integer)
      languageId => 0 (integer)
      typeName => 'example_element1' (16 chars)
      updateDate => 1694625077 (integer)
      creationDate => 1694602137 (integer)
      header => 'Foo' (3 chars)
Copied!

As you can see, 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. The object consists of two properties _raw and _processed. As the names suggest, the one is raw and unprocessed and the other one has magic applied from Content Blocks. Normally you would access the processed properties. This is done by simply accessing the desired property like {data.header}. Note, that we are omitting _processed here. This is important to remember, as this would access a custom field named _processed. On the other hand, the raw properties have to be accessed by {data._raw.some_field}. But most of the time you shouldn't need them.

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:

<!-- Normal access to custom properties -->
{data.my_field}

<!-- 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>

<!-- There are some special accessors, which are always available: -->
{data.uid}
{data.pid}
{data.typeName} <!-- This is the CType for Content Elements -->

<!-- These special accessors are available, if the corresponding features are turned on (Always true for Content Elements) -->
{data.languageId} <!-- YAML: languageAware: true -->
{data.creationDate} <!-- YAML: trackCreationDate: true -->
{data.updateDate} <!-- YAML: trackUpdateDate: true -->

<!-- These special accessors are available depending on the context -->
{data.localizedUid}
{data.originalUid}
{data.originalPid}

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

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 Source/Frontend.html and the backend template in Source/EditorPreview.html.

Asset ViewHelpers

Content Blocks provides new asset ViewHelpers to access assets from within the current Content Block in the template. These ViewHelpers look for the given file in the Assets directory.

<f:comment><!-- Include the Assets/Frontend.css stylesheet --></f:comment>
<cb:asset.css identifier="myCssIdentifier" file="Frontend.css"/>

<f:comment><!-- Include the Assets/Frontend.js script --></f:comment>
<cb:asset.script identifier="myJavascriptIdentifier" file="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>
<cb:asset.script identifier="myJavascriptIdentifier" name="vendor/name" file="Frontend.js"/>
Copied!

Resource URI ViewHelper

The ViewHelper can be used to generate a URI relative to the Assets folder.

<img src="{cb:uri.resource(path: 'Icon.svg')}" alt="">
Copied!

To generate an absolute URI, activate the absolute parameter.

<img src="{cb:uri.resource(path: 'Icon.svg', absolute: '1')}" alt="">
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:

<img src="{cb:uri.resource(path: 'Icon.svg', name: 'vendor/name')}" alt="">
Copied!

Translation ViewHelper

This ViewHelper looks directly in the Labels.xlf file for the given key.

<cb:translate key="my.contentblock.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:

<cb:translate key="my.contentblock.header" name="vendor/name" />
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 Source 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.html" 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 Source 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.

Adding new groups

There are two types of groups. One is for the NewContentElementWizard. This is where you can select Content Elements from a module with tab navigation. Each tab item is a group. This is the group, which you define in the YAML config.

The second type is the one in the type selector box. Also called record type selector. The select items are grouped there with TCA itemGroups. The v12 version of Content Blocks hard-codes this group to "Content Blocks".

In TYPO3 v12

In v12 you have to add a group with page TSconfig.

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!

Now you can assign the new group my_group to the YAML group option.

In TYPO3 v13

In v13 you need to register the group via PHP API. This will add a new group both for the NewContentElementWizard and the type selector box.

EXT:my_package/Configuration/TCA/Overrides/tt_content.php
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItemGroup(
    'tt_content',
    'CType',
    'my_group',
    'My group label or LLL:EXT reference',
    'before:default',
);
Copied!

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

The identifier to use in TCA depends on whether the Content Block uses prefixFields or not. If this feature is enabled, your field identifiers are prefixed with the vendor and content block name. Example: my-vendor/my-content-block and field identifier header result in myvendor_mycontentblock_header. See how dashes are removed and the two parts are glued together with an underscore. The same goes for the table name of Collection fields. myvendor_mycontentblock is also the resulting typeName, if not set explicitly. This can be used to override the TCA types array. Otherwise, the field and table identifiers defined in the YAML config are identical to the TCA one.

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 EditorInterface.yaml.

Example:

tt_content {
    myvendor_mycontentblockname {
        layoutRootPaths {
            20 = EXT:site_package/ContentBlocks/ContentElements/content-element-name/Source/Layouts/
        }
        partialRootPaths {
            20 = EXT:site_package/ContentBlocks/ContentElements/content-element-name/Source/Partials/
        }
        templateName = Frontend
        templateRootPaths {
            20 = EXT:site_package/ContentBlocks/ContentElements/content-element-name/Source/
        }
    }
}
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 {
        10 = menu
        10 {
          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 {
        10 = menu
        10 {
          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 EditorInterface.yaml file. These are already shipped by Content Blocks.

EXT:your_extension/ContentBlocks/ContentElements/fsc-test/EditorInterface.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/Source/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.

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/EditorInterface.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/Source/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/Source/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/Source/BackendPreview.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.

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.

lib.parseFunc_RTE

In order to process links inside RTE fields, one needs to define a so called parseFunc TypoScript snippet. This snippet is shipped in the Core, when you use the fluid_styled_content system extension. If you only rely on Content Blocks, you need to define it yourself.

There are multiple options. You can just simply copy the snippet from fluid_styled_content and substitute the constants with your own values. Just remember to look for changes after major TYPO3 releases. There might be new or deprecated options.

Another option could be to use a snippet from popular ready-to-go sitepackages like bootstrap_package. However, these tend to be out of date so you need to check yourself, if it does fit your (security) needs.

Plugins and contentRenderingTemplates

Plugins from other extensions probably won't work without fluid_styled_content as the rendering definition 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}"/>
}

# @todo snippet for CType based plugins.
Copied!

The TypoScript 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.

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

This is a guide. For YAML reference refer to this page.

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.

Use as Frontend template

Historically, the backend layout / page layout is used as the switch for frontend templates and is still considered best practice. This means you have to define an additional page layout for each new Page Type and assign it.

An alternative, more modern approach is to map the frontend template to the Page Type directly. This makes it possible to have different backend 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!

Backend preview

Just like for Content Elements, you can define an EditorPreview.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.tableName}" 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 overlayed with another icon. As for now, only the "hide in menu" state can be supplied with a custom icon. Put an icon with the name IconHideInMenu.svg inside your Assets folder to use it.

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. Right now, this does not resolve relations for the native, standard Page Type.

page {
    10 = FLUIDTEMPLATE
    10 {
        dataProcessing {
            1 = content-blocks
        }
    }
}
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/EditorInterface.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/EditorInterface.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/EditorInterface.yaml
name: example/diver
table: person
typeField: type
typeName: diver
fields:
  - identifier: title
    type: Text
Copied!
EXT:your_extension/ContentBlocks/RecordTypes/instructor/EditorInterface.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/EditorInterface.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/EditorInterface.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/EditorInterface.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!

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
  • assets
  • image
  • media
  • categories
  • pages

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

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 EditorPreview.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!

Developer corner

The Content Blocks Content Types API also offers some interfaces for developers.

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 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.

interface FieldTypeInterface
{
    public static function createFromArray(array $settings): FieldTypeInterface;
    public function getTca(): array;
    public function getSql(string $column): string;
    public static function getName(): string;
    public static function getTcaType(): string;
    public static function isSearchable(): bool;
    public static function hasItems(): bool;
}
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.

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.

getTcaType

The TCA type, the new Content Blocks type is based on.

isSearchable

Whether the field contents should be searchable in global search.

Example

Example for a field type "Money".

<?php

declare(strict_types=1);

namespace VENDOR\MyExtension\FieldType;

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

final class MoneyFieldType implements FieldTypeInterface
{
    use WithCommonProperties;

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

    public static function getName(): string
    {
        return 'Money';
    }

    public static function getTcaType(): string
    {
        return 'number';
    }

    public static function isSearchable(): bool
    {
        return false;
    }

    public static function createFromArray(array $settings): self
    {
        $self = new self();
        $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'] = self::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;
    }

    public function getSql(string $column): string
    {
        $null = ' NOT NULL';
        if ($this->nullable) {
            $null = '';
        }
        return "`$column` decimal(10,2) DEFAULT '0.00'" . $null;
    }
}
Copied!

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.

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.

Manual migration

As of now, there is no automatic migration from Mask to Content Blocks. This might come in the future. For now, let's see how to do the migration manually. The advantage is that you might better understand how the extension works. 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/EditorInterface.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/EditorInterface.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 Frontend.html and you are done. That said, if you didn't use any partials or layouts. If so, move these to the according Source/Partials and Source/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/EditorInterface.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
Copied!

The limitation is, that you can't add constraints to the available CTypes in the child field. However, you can set a default value.

EXT:site_package/ContentBlocks/ContentElements/nested-content/EditorInterface.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
    overrideChildTca:
      columns:
        CType:
          config:
            default: text
Copied!

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.

Known Problems

On save error: Row size too large (MariaDB)

Explanation

There is a limit on how much can fit into a single InnoDB database row. Read here for more technical insight. As Content Blocks uses the table tt_content, it must be ensured, that the table does not grow indefinitely.

Solutions

First, check if you are using the DYNAMIC row format. If not, alter your tables to use this format, in order to store more data on overflow pages.

ALTER TABLE tt_content ROW_FORMAT=DYNAMIC;
Copied!

Else, here are some tips to save table row size:

  • Reuse existing TYPO3 core and Content Blocks fields as much as possible.
  • Try to minimize the usage of new Text, Link, Email, Radio, Color and Select fields. They all use varchar(255), Link fields even use varchar(1024).
  • You can change varchar fields to text, as suggested here.
  • If applicable, use Collections, as they create a new table.
  • Otherwise consider creating an own extension with custom tables if your Content Blocks are getting too complex.

Read this mariadb troubleshooting guide 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 EditorPreview.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
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 EditorInterface.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 EditorInterface.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 (EditorPreview.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 EditorInterface.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