Neos CMS offers the possibility to render dynamic pages completely without Fluid by using Fusion.
In this example, we show how to:
- define custom URLs via Routes.yaml
- call a controller
- load a Node depending on the language
- render a complete page with Fusion
- display events dynamically
- generate hreflang links via Fusion
The controller remains very small. Fusion handles the actual rendering.
1. Custom URLs with Routes.yaml
First, we define custom URLs such as:
/en/2025/my-event /de/2025/mein-event /2025/mein-event
The Routes.yaml can look like this, for example:
- name: 'Event Detail with Language'
uriPattern: '{language}/{year}/{slug}'
defaults:
'@package': 'Vendor.Site'
'@controller': 'Event'
'@action': 'show' appendExceedingArguments: true
- name: 'Event Detail default Language'
uriPattern: '{year}/{slug}'
defaults:
'@package': 'Vendor.Site'
'@controller': 'Event'
'@action': 'show' language: 'de' appendExceedingArguments: true
This gives us the parameters language, year, and slug, which are passed to the controller.
2. The Controller
The controller only loads the appropriate Node and passes it to Fusion.
final class EventController extends ActionController
{
protected $defaultViewObjectName = FusionView::class; #[Flow\Inject] protected ContextFactoryInterface $contextFactory; public function showAction(string $language, string $slug): void {
$context = $this->contextFactory->create([
'workspaceName' => 'live',
'dimensions' => ['language' => [$language]],
]);
$eventNode = $context->getNode(
'/sites/mysite/events/' . $slug
);
$this->view->assign('eventNode', $eventNode);
$this->view->assign('language', $language);
$this->view->assign('slug', $slug);
}
}
The controller contains no HTML and no layout. It only provides the data.
3. The Layout with Fusion
Fusion handles the complete rendering of the page.
prototype(Vendor.Site:Layout.ControllerPage) < prototype(Neos.Fusion:Component) {
node = null
innerContent = ''
renderer = Neos.Fusion:Join {
doctype = '<!DOCTYPE html>'
html = afx`
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>{q(props.node).property('title')}</title>
<link
rel="stylesheet"
href="/_Resources/Static/Packages/Vendor.Site/Css/style.css"
/>
</head>
<body>
{props.innerContent}
</body>
</html>
`
}
}
This creates a complete HTML page directly with Fusion.
4. Displaying Event Content
For the event, we create a separate Fusion component:
prototype(Vendor.Site:Document.EventPage) < prototype(Neos.Fusion:Component) {
node = ${node}
renderer = afx`
<article class="event-detail">
<h1>{q(node).property('title')}</h1>
<p class="event-date">
{q(node).property('eventDate')}
</p>
<div class="event-description">
{q(node).property('description')}
</div>
</article>
`
}
The event page is then inserted into the layout:
Vendor.Site.EventController.show = Vendor.Site:EventController.Show prototype(Vendor.Site:EventController.Show) < prototype(Neos.Fusion:Component) {
@context {
node = ${eventNode}
}
renderer = Vendor.Site:Layout.ControllerPage {
node = ${node}
innerContent = Vendor.Site:Document.EventPage {
node = ${node}
}
}
}
The process is simple: the controller loads the Node, Fusion renders the layout, and Fusion displays the event content.
5. Multilingual hreflang Links
hreflang links can also be generated directly in Fusion.
prototype(Vendor.Site:AlternateLanguages) < prototype(Neos.Fusion:Component) {
node = ${node}
supportedLanguages = ['de', 'en']
renderer = Neos.Fusion:Loop {
items = ${props.supportedLanguages}
itemName = 'lang'
itemRenderer = afx`
<link rel="alternate" hreflang={lang} href={EventHelper.getEventUrl(props.node, lang)}
/>
<link rel="alternate" hreflang="x-default" href={EventHelper.getEventUrl(props.node, 'de')} @if={lang == 'de'}
/>
`
}
}
The component is included in the layout like this:
<Vendor.Site:AlternateLanguages node={node} />
Conclusion
With Routes.yaml, a small controller, and Fusion, dynamic pages in Neos can be implemented completely without Fluid. The controller only loads the data. Fusion handles the layout, content, and SEO links.
This makes it possible to implement custom URLs, multilingual content, and dynamic pages such as event detail pages cleanly with Neos.
