novactive/ezstatictemplatesbundle
An eZ Platform bundle that maps URLs to Twig templates using siteaccess and the design engine. Access a siteaccess in the static_group and the matching template path is rendered automatically, with no configuration required.
Install
composer require novactive/ezstatictemplatesbundle
Register the bundle in config/bundles.php (or app/AppKernel.php on older eZ Platform):
Novactive\Bundle\eZStaticTemplatesBundle\NovaeZStaticTemplatesBundle::class => ['all' => true],
Wire up a siteaccess in the static_group
The bundle only activates for siteaccesses belonging to a group named exactly static_group. In config/packages/ezplatform.yaml:
ezplatform:
siteaccess:
list: [site, static]
groups:
site_group: [site]
static_group: [static]
match:
static:
URIElement: 1 # or a dedicated host/port
site:
URIElement: 1
Give the static siteaccess its own design/theme (or reuse the main one) so the design engine can resolve your Twig files.
First use case
Create a template under your theme's template directory, e.g. templates/static/hello.html.twig, then hit the matching path on the static siteaccess URL — e.g. https://static.localhost/hello should render it directly, with the current siteaccess injected into the Twig context. Iterate on markup and reload; no content tree, no controller, no route definition.
Where to look first
.md docs (and the rendered docs at novactive.github.io/Nova-eZPlatform-Bundles/.../StaticTemplatesBundle/).Resources/config/ — that's where the URL-to-template mapping convention is defined.Verify the exact URL→template convention against the docs before committing to a folder structure; don't guess, since a wrong assumption silently 404s.
Static design preview while the content model is in flux
This is the primary value. When front-end work starts before content types, fields, and view templates exist, you can build the full page as a plain Twig file, preview it at a URL, and only later point the real content_view configuration at it. Keep the static template as the single source of truth and have the content view template {% include %} it once the model settles — that way nothing is thrown away.
Component/pattern library by URL
One template per component, one URL per component:
/components/card → templates/components/card.html.twig
/components/hero → templates/components/hero.html.twig
/pages/article → templates/pages/article.html.twig
Front-end devs and designers get a stable, bookmarkable index of the design system without needing a CMS editor account or sample content.
Dedicated host, not a path prefix
Use a separate hostname (static.localhost) or port for the static siteaccess rather than a URI prefix. A prefix collides with real site routes and makes relative asset/link generation confusing. A distinct host keeps static_group completely isolated from production traffic.
Reuse partials with relative includes
Because the resolution is path-based, structure templates so partials are reachable relative to the entry template. Keep a shared _layout.html.twig at the theme root and include it with a relative or @ezdesign/... path from every static entry template. This keeps the same files usable later from content view templates.
Asset handling
Standard Symfony asset() and the design engine's asset resolution both work, but the static siteaccess must point at a design that actually contains those assets. If images/CSS 404 while the HTML renders, the design mapping for the static siteaccess is wrong — that's the first thing to check, not the bundle.
Smoke-test in CI
Once you have a list of static templates, add a cheap HTTP check (curl or a Symfony WebTestCase) that asserts each path returns 200. Since these templates bypass the content tree, a broken Twig or a renamed file only surfaces when someone loads the URL — a test list catches it early.
The group name is hardcoded
static_group is a literal string in the bundle. A typo (static_site_group, static-templates) means the bundle silently does nothing and you fall through to eZ's normal 404. If your URLs 404 with no error, check the group name first.
Treat this as a development-only feature
It renders Twig templates purely from request path — anyone who can guess a path can render it. Do not leave a static_group siteaccess exposed on production. Either omit the siteaccess from the production config entirely, or put it behind an access control rule / IP allow-list. This is the most important operational caveat of the package.
Design engine resolution is non-negotiable
The bundle delegates template lookup to the eZ design engine (@ezdesign). Templates dropped outside the theme's declared template path will not resolve, even if the path "looks right". Debug with bin/console ezplatform:debug:siteaccess (or ibexa: equivalent) to confirm which design the static siteaccess is actually using.
Route priority and collisions
The bundle installs a fallback route. Existing application routes take precedence, so a static template path that collides with a real route will never render. Prefix static template directories (e.g. static/…) if you hit this.
Path conventions: index files, trailing slashes, extensions
Verify how /foo/ vs /foo and directory-style paths (/foo/index) are resolved. Path normalization quirks are the most common source of "it works for a/b but not a/b/c" confusion. Also confirm whether the .html.twig extension is implicit or inferred (e.g. .twig vs .html.twig).
Caching will bite you
In dev, ezplatform.http_cache must be off and Twig cache cleared (bin/console cache:clear) after template edits. In a preview/staging environment with HTTP cache on, stale HTML is the default symptom of "my change didn't appear".
Siteaccess auto-injection changes context
Because the current siteaccess is injected, ezpublish.siteaccess / ibexa.siteaccess is available in templates without extra work — convenient for language/root-based conditionals. But content lookups (ez_render, content queries, route helpers) behave differently or fail with no content behind them. Keep static templates free of content-dependent calls, or guard them.
Maturity risk
Zero stars and no dependents means very little community use. The repo is a Composer subtree split: issues and PRs belong in Novactive/Nova-eZPlatform-Bundles, not the split repo — filing in the wrong place gets ignored. Pin the version in composer.json, and read the bundle's controller once so you own the URL-resolution logic if it breaks. If the package lags your eZ/Ibexa major version, the fallback controller is small enough to vendor a copy into your own app, which removes the dependency risk entirely.
How can I help you explore Laravel packages today?