Creating a theme from scratch
A Thelia 3 front-office theme is a Symfony bundle. The Flexy theme ships as FlexyBundle: a class extending AbstractBundle that auto-loads its own services, Twig/Live components, controllers and assets. Template files alone are not enough. A real theme needs its Bundle class so Symfony can wire everything together.
This guide walks through the pieces of a theme, using Flexy as the reference, and shows how to build your own.
Prerequisites
- A working Thelia 3 installation (see Installation).
- Familiarity with Twig basics in Thelia and data access.
A front-office theme served through AssetMapper needs no Node.js and no bundler. Flexy is one, and the guide below follows it.
A theme is a bundle
The Flexy theme registers itself as a Symfony bundle. Services and controllers are autowired from src/, components from components/, and both directories are declared as PSR-4 roots in the theme's composer.json:
{
"autoload": {
"psr-4": {
"FlexyBundle\\": "src/",
"FlexyBundle\\Components\\": "components/"
}
}
}
// templates/frontOffice/flexy/src/FlexyBundle.php
namespace FlexyBundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
class FlexyBundle extends AbstractBundle
{
public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
{
$container->import('../config/services.yaml');
$container->services()
->defaults()
->autowire()
->autoconfigure();
}
public function prependExtension(ContainerConfigurator $container, ContainerBuilder $builder): void
{
$builder->prependExtensionConfig('twig', [
'paths' => [
\dirname(__DIR__).'/components' => 'Flexy',
\dirname(__DIR__).'/form' => 'FlexyForm',
],
'globals' => [
'flexy_form_themes' => ['@FlexyForm/flexy_form_theme.html.twig'],
],
]);
$builder->prependExtensionConfig('twig_component', [
'anonymous_template_directory' => 'frontOffice/%thelia_front_template%/components/',
'defaults' => [
'FlexyBundle\\Components\\' => [
'template_directory' => '@Flexy',
'name_prefix' => '',
],
],
]);
// AssetMapper, UX Icons, Tailwind and Stimulus are configured the same way.
}
}
What this does:
loadExtension()importsconfig/services.yaml, which registers both PSR-4 roots as autowired, autoconfigured services.autoconfigure()is what makes#[Route]controllers,#[AsTwigComponent]and#[AsLiveComponent]classes work without any XML.prependExtension()configures the framework for the theme: Twig namespaces and globals, the TwigComponent defaults, AssetMapper, UX Icons, Tailwind and the Stimulus controller paths.name_prefix: ''is what makes component names carry no prefix.- Keys that must follow the active front-office template are written with the
%thelia_front_template%parameter, so a theme that is installed but not active does not register its own paths.
The bundle is enabled in config/bundles.php:
// config/bundles.php
return [
// ...
FlexyBundle\FlexyBundle::class => ['all' => true],
];
For your own theme, create a MyThemeBundle class following this pattern (adjust the namespaces and the two psr-4 entries in composer.json), register it in config/bundles.php, and point its Twig and component paths at your theme directory. Without a Bundle class, controllers, Twig components and Live components in your theme will never be discovered.
Directory structure
The Flexy theme lives in templates/frontOffice/flexy/. A theme combines flat page templates at the root, a components/ tree, a src/ PHP tree (the bundle code) and an assets/ pipeline:
templates/frontOffice/my-theme/