Mapping Sulu Page Templates to Controllers and Twig for Localized Rendering
Learn how Sulu maps page templates to Twig files and optional controllers, and verify the routing for multi-locale sites with a concrete landing-page example.
25 Oct 2025, 16:43 UTC

The problem: pages that look right in one locale but break in another
When a Sulu site serves multiple languages, each page is stored once per locale but shares a single structure tree. The frontend must pick the correct Twig template and the correct controller (if any) for the current locale without hard-coding paths. If the mapping between a page template key, its Twig file, and an optional controller is mis-configured, editors see the right content in the admin but the public site renders a 404 or falls back to the default template.
How Sulu resolves a page request
- The WebsiteResolver extracts the website and locale from the request (host, prefix, or domain).
- The router matches a route like /{_locale}/page/{id} and forwards to Sulu\Bundle\WebsiteBundle\Controller\DefaultController::indexAction.
- The controller loads the Page object for the current locale via the PageRepository.
- The page's template property (e.g. default, landing) is looked up in config/packages/sulu_page.yaml.
- That configuration entry points to a Twig template (e.g. templates/pages/default.html.twig) and optionally a controller service.
- If a controller is defined, Sulu calls it with the page and locale; otherwise the Twig template receives the page object directly.
Concrete worked example: a landing template with a custom controller
1. Define the template in sulu_page.yaml
# config/packages/sulu_page.yaml
sulu_page:
templates:
landing:
controller: App\Controller\LandingController::index
template: "pages/landing.html.twig"
type: default
positions:
content:
type: content
Run this on the Symfony project root (requires write access to config/packages/). The controller key is optional; omit it for pure Twig rendering.
2. Create the Twig template
{# templates/pages/landing.html.twig #}
{% extends 'base.html.twig' %}
{% block body %}
{{ page.getTitle() }}
{{ page.getExcerpt() }}
{{ page.getContent()|raw }}
{% endblock %}
Place the file under templates/pages/ (standard Symfony template directory). No special permissions beyond the usual web-server read access.
3. Implement the optional controller
get('translator')->trans('landing.cta', [], 'messages', new \Locale($locale));
return $this->render('pages/landing.html.twig', [
'page' => $page,
'cta' => $cta,
]);
}
}
Run this in the src/Controller/ directory (requires PHP write permission). The controller receives the resolved PageDocument and the current locale automatically.
4. Verify routing and template resolution
# Run from the project root (requires CLI access)
php bin/console debug:router | grep page
Expected output includes a route like sulu_page_index ANY /{_locale}/page/{id}. Confirm the _locale placeholder is present; its absence signals a mis-configured WebsiteResolver.
5. Test in the admin
- Edit any page, change its Template dropdown to Landing.
- Save and preview the page in each configured locale (e.g. /en/page/12, /de/page/12).
- Check that the hero title, excerpt, and the injected cta variable appear correctly.
Trade-off: custom controller vs. pure Twig
Using a controller gives you full PHP logic (service calls, form handling, locale-specific data) but adds a class to maintain and a potential point of failure if the controller signature drifts from Sulu's expectations. Pure Twig keeps the rendering declarative and leverages the page object's getters (getTitle(), getContent(), getProperty('foo')) which are already locale-aware. Choose a controller only when you need data that cannot be expressed as a page property.
Limitations and version notes
- Configuration keys (templates, controller, template) are stable in Sulu 2.x; Sulu 1.x used sulu_page.templates with a different nesting. Verify the installed major version before copying the snippet.
- The WebsiteResolver must be enabled (default in the website bundle). Disabling it removes locale detection and breaks the _locale route parameter.
- Preview mode in the admin uses a separate request cycle; ensure the controller does not rely on session-only data that the preview user lacks.
Actionable checklist
- Open config/packages/sulu_page.yaml and confirm each template key maps to an existing Twig file.
- Run php bin/console debug:router and verify the page route contains {_locale}.
- In the admin, switch a page's template to the new key and preview in every locale.
- If a custom controller is used, add a functional test that calls the controller with a mocked PageDocument and asserts the rendered template name.
Following these steps helps editors change templates freely while the frontend consistently serves the correct localized markup.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.