In Contao, Content Elements are the fundamental content blocks. In its simplest
form it is a fragment controller which receives data in form of a content model
and returns a response.
These elements are implemented as so called fragment controllers which Contao
then renders into the main content, using their defined renderer. See the caching documentation
for more information.
Creating a content element is very similar to creating front end modules.
Definition
To create a new content element, the following things must be defined and implemented:
Fragment Controller The actual implementation of the content element is done via a class that extends
from AbstractContentElementController of the Contao core.
Service Tag To identify the controller as a Contao content element, the service must be tagged
with the service tag contao.content_element - and the tag will hold the following additional information:
Type The type of a content element is a specific string which is used to identify
the element’s template (if not defined) and DCA palette. If omitted the type will be
automatically inferred by converting the class name of the controller from pascal case to snake case and removing a
possible Controller postfix.
Category All content elements are categorised within the type dropdown of the content element’s palette. The default category
is miscellaneous when using PHP attributes to tag the service.
Template If not specified, the Twig template name follows is the type and prepends it with the
content_element/ path, i.e. content_element/<type>.html.twig.
Example
Consider this very simple example of a content element:
In this example the service tag was implemented via PHP attributes (see the different registration types
below).
In order to be able to set the options for this content element and fill it with content in the back end, we also need
to define a specific palette in the tl_content DCA configuration. The palette key is based on the type
of the content element. Since we did not specify a type in our example it defaults to example_element as explained
above.
This very simple palette enables a back end user to fill the (pre-existing) field text via the create and edit view of
this content element. Now we create a basic Twig template for the output:
A “fragment template” instance of this template will automatically be generated and passed to the controller’s
getResponse() method. The controller then returns the rendered Twig template as a response.
Registration
As mentioned previously a content element is registered by registering a controller as a service and tagging it with the
contao.content_element service tag. The service tag supports the following options:
Option
Type
Description
name
string
Must be contao.content_element.
type
string
Optional: The type mentioned in Type can be customized.
category
string
Defines in which option group this content element will be placed in the content element selector.
template
string
Optional: Override the generated template name.
renderer
string
Optional: The renderer can be changed to inline or esi. Defaults to forward. See Caching Fragments for more details.
method
string
Optional: Which method should be invoked on the controller.
The above example only defines the category attribute (which is actually optional for the PHP attribute - the default
category is miscellaneous). If you wish you can also define the other options of the service tag:
However, it is recommended to only define what you need and otherwise leave the defaults.
A content element can be registered using the ContentElement annotation. The annotation can be used on the class of the content element,
if the class is invokable (has an __invoke method) or extends from the AbstractContentElementController. Otherwise the annotation can be
used on the method that will deliver the response.
However, it is recommended to only define what you need and otherwise leave the defaults.
Translations
In order to have a nice label in the back end, we also need to add a translation
for our content element - otherwise it will only be named example_element.
The translation needs to be set as follows:
# translations/contao_default.en.yamlCTE:example_element:- My Content Element- A Content Element for testing purposes.
Page Model
If your fragment extends from AbstractContentElementController (or just AbstractFragmentController)
you can use $this->getPageModel() in order to receive the \Contao\PageModel
object of the currently rendered page of Contao’s site structure.
// src/Controller/ContentElement/ExampleElementController.php
namespaceApp\Controller\ContentElement;useContao\ContentModel;useContao\CoreBundle\Controller\ContentElement\AbstractContentElementController;useContao\CoreBundle\DependencyInjection\Attribute\AsContentElement;useContao\CoreBundle\Twig\FragmentTemplate;useSymfony\Component\HttpFoundation\Request;useSymfony\Component\HttpFoundation\Response;#[AsContentElement(category: 'texts')]
classExampleElementControllerextendsAbstractContentElementController{protectedfunctiongetResponse(FragmentTemplate$template,ContentModel$model,Request$request):Response{$page=$this->getPageModel();// Get some information about the current page
$template->set('rootTitle',$page->rootPageTitle?:$page->rootTitle);return$template->getResponse();}}
Nested Fragments
Info
This feature is available in Contao 5.3 and later.
An alternative approach to the aforementioned wrapper elements are so called “nested fragments”. These allow you to
nest other content elements within a parent content element for which nested fragments are enabled. The following
screenshot shows the Element group content element of the Contao Core:
Just as with page articles, news archives, etc. this content element allows you to edit its children via
which then works the same way as within the
article of a page.
Allowing nested fragments for your content element works via the nestedFragments option in the service tag:
With nested fragments it is also possible to restrict the fragment’s children to specific content element types. Say you
want to implement a specific slider content element which should only allow images and videos as its children. Then
instead of defining true for the tag’s nestedFragments option you can instead pass an additional option called
allowedTypes:
Now all you have to do is render these fragments in your template. The AbstractContentElementController from which
your content element likely extends from automatically passes the available nested fragments to your template within
the nested_fragments template variable. This will be a collection of ContentElementReference objects which you can
render via the content_element() Twig function:
Before the introduction of nested fragments in Contao, there were special content elements called “wrappers” which you
insert before and after one or a group of content elements. These wrappers affect the back end view, indicating that all
elements contained within the two wrappers are descendants of the parent wrapper. The wrapper content elements typically
consist of a startand stop element, though there are also wrappers of type single and separator. The start
element typically opens a specific HTML tag, while the stop element will close it again.
In order to define that a content element is a wrapper of a specific type, it needs to be registered in the
$GLOBALS['TL_WRAPPERS'] array in your contao/config/config.php. The $GLOBALS['TL_WRAPPERS'] array holds the
element types for each type of wrapper. For example:
Make sure to output different HTML markup in the back end for your wrapper elements, otherwise you will break the back
end, since your wrapper templates will likely either only open or closes a tag. You can also output no content at all: