Building a Reusable Custom Content Element in Contao Using Frontend Modules and Template Overrides
Learn how to create a custom rotating banner in Contao using frontend modules and template overrides without modifying core files.
23 Aug 2025, 06:34 UTC

Problem: Need a dynamic banner without touching core
You want a rotating banner that picks an image from a custom table each time a page is rendered. Editing Contao’s core files is not an option because upgrades would overwrite your changes and the solution would be hard to reuse across projects.
Thesis: Leverage Contao’s frontend module system and template overrides
Contao treats every content element as a frontend module. By extending the Module class, implementing the generate() method, and registering the class in config.php, you create a self‑contained module. Designers can then adjust the markup by placing a template override in the /templates/modules/ folder, leaving the PHP logic untouched.
Creating the module class
Create a PHP class in your extension’s src/Module/ directory. The example below reads image IDs from a custom table tl_banner_images, shuffles them, and passes the selected file path to the template.
// src/Module/RotatingBanner.php
namespace MyExtension\Module;
use Contao\Module;
use Contao\BackendTemplate;
use Contao\Database;
class RotatingBanner extends Module
{
protected $strTemplate = 'mod_rotating_banner';
public function generate()
{
if (TL_MODE == 'BE') {
$objTemplate = new BackendTemplate('be_wildcard');
$objTemplate->wildcard = '### Rotating Banner ###';
$objTemplate->title = $this->name;
$objTemplate->id = $this->id;
$objTemplate->link = $this->name;
$objTemplate->href = 'contao/main.php?do=themes&table=tl_module&act=edit&id='.$this->id;
return $objTemplate->parse();
}
// Fetch image IDs from the custom table where pid equals the module’s page ID
$arrImages = [];
$objResult = Database::getInstance()->prepare("SELECT id FROM tl_banner_images WHERE pid=?")
->execute($this->pid);
while ($objResult->next()) {
$arrImages[] = $objResult->id;
}
if (empty($arrImages)) {
return '';
}
// Shuffle and pick the first image
shuffle($arrImages);
$selectedId = $arrImages[0];
// Retrieve the file path from tl_file
$objFile = Database::getInstance()->prepare("SELECT path FROM tl_file WHERE id=?")
->execute($selectedId);
if ($objFile->next()) {
$this->strImage = $objFile->path;
} else {
$this->strImage = '';
}
return parent::generate();
}
}
Registering the module
In your extension’s config.php add the module to the global TL_MODULES array:
// config.php
$GLOBALS['TL_MODULES']['rotating_banner'] = 'MyExtension\Module\RotatingBanner';
Providing a template override
Create the file templates/modules/mod_rotating_banner.html5 inside your extension (or in the project’s /templates folder to override it per‑site). A simple markup example:
<?php if ($this->strImage): ?>
<img src="{{ env::base . $this->strImage }}" alt="Rotating banner" />
<?php endif; ?>
Designers can edit this file without touching the PHP class, keeping logic and presentation separate.
Worked example: adding the banner to a page
- Install the extension via the Contao Extension Manager (backend → System → Extension Manager).
- Clear the internal cache (System → Maintenance → Internal cache → Clear) so the new module is recognized.
- Navigate to the page layout, add a new module, and select “Rotating banner” from the list.
- Save the module, publish the page, and visit the frontend.
- Each page load should display a different image from
tl_banner_images(assuming the table contains IDs).
If the image does not appear, check the Contao log (var/logs) for errors and verify that the custom table exists and contains valid tl_file IDs.
Trade‑off and limitation
- Cache clearing required – After adding or changing the module class or its template, you must clear Contao’s internal cache; otherwise the frontend may show the old version or no output at all.
- Potential API changes – Future major Contao releases could adjust the
Moduleclass or thegenerate()signature. Review the release notes when upgrading and update your extension accordingly.
Actionable closing
By following the steps above you have a fully reusable, upgrade‑safe content element that can be packaged as a Contao extension and dropped into any project. Start with a minimal version, test the image selection logic, then enrich the module with additional settings (e.g., transition effects, custom CSS classes) as needed.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.