Core dna renders server-side sites with Smarty or with Twig, one engine per site, and treats the two as peers. This guide is the one place both are documented: what they share, each engine's language and the platform's additions and limits, the two side by side, and translation.
L1–L2
Unit 01 · The shared model
Template resolution order
The shared modelPage 2 of 25Updated
Every template name is looked up in three folders, the site's own first, and the first match wins, in both engines. How one request resolves, how a site replaces a platform template by putting a file at the same path, why there is no frontend segment in the path, and what a missing template looks like.
Who this is for. The theme developer who includes partials, extends a base template or wants to change a screen the platform ships, such as the sign-in page. Afterwards you can predict which file renders for any template name, override a platform template safely, and tell a missing template from a broken one.
Three folders, first match wins
When the platform renders a template, or a template includes or extends another by name, the engine looks for the name in three folders, in this order, and uses the first file it finds:
The site's templates/ folder: your theme repository.
The platform's templates/ folder: the templates every site shares, such as the admin, the default sign-in page and the generated documentation.
The site's virtual files folder (.virtual-files), where the platform keeps files stored through the Files module. It rarely supplies a template; it is here so the order is complete.
Both engines are given the same three folders in the same order, so the chain is identical on a Smarty site and a Twig site. A name is always written from the top of a templates/ folder, without a leading slash: components/card.html, never /templates/components/card.html.
One request, through all three
A visitor opens /login. The route sends the request to the Users module's sign-in controller, whose template is users/login.html. The engine then looks:
Step
Looks for
On a site that has not overridden it
On a site that has
1
<site>/templates/users/login.html
Not there
Found: renders
2
<platform>/templates/users/login.html
Found: renders
Never reached
3
<site>/.virtual-files/users/login.html
Never reached
Never reached
A page whose layout names pages/pages/show.html goes through the same three steps, but the platform ships no file by that name, so a site without one reaches the end of the chain with nothing found (below). The layout, not the URL, decided the name; Which template renders a record covers that half.
Overriding a platform template
To replace a template the platform ships, create a file at the same relative path in your site's templates/ folder. Step 1 finds yours, and every other site keeps the platform's. Start from a copy of the platform's file so you keep what it does, then change it. A minimal sign-in page, at templates/users/login.html:
The base template and the messages partial are your site's own. The variables (user, localPasswordEnabled, enabledOAuthAuthenticators) are the ones the sign-in controller assigns, whichever template renders. Registration, sign-in and passwords covers the whole sign-in contract, including passkeys.
Three things come with an override:
You maintain it from now on. When the platform changes its version, adding a field or a passkey button, your copy does not change. Note in the file which platform template it replaces, so whoever upgrades the site knows to compare.
On a Twig site, overriding is not optional for some screens. The platform's users/login.html is written in Smarty and has no Twig version, so a Twig site that uses the platform's sign-in route provides its own. The screens that do exist in both engines carry a -twig suffix, and a Twig site overrides that file (Two engines, one platform).
Error pages are a special case. For errors/not-found.html, errors/fatal-error.html, errors/not-authorized.html and errors/bad-request.html, the platform checks only the site's folder. When the site has the file, it is rendered through the engine with the usual variables; when it does not, the platform sends its own file as plain HTML, without rendering it.
Full names and relative paths
In Smarty, a name that starts with ./ or ../ is resolved against the folder of the file doing the including, and the chain is not searched. That matters when you override one file of a set. The platform's includes/frontend/messages/all.html includes its siblings as ./success.html, ./error.html and ./warning.html, so a site that overrides only success.html sees no change: the platform's all.html still includes the platform's success.html. Override all.html as well, and its ./ includes then find your copies.
Twig has no relative names: ./card.html is looked up as a name in the three folders, and is not found. Write every Twig name in full from the top of templates/.
Neither engine loads a file outside the three folders. Smarty answers an absolute path elsewhere with Smarty Security: not trusted file path, and Twig answers a ../ that climbs out with Looks like you try to load a template outside configured directories.
No frontend segment
A module controller that is not given a template builds its name from the module, the entity and the action: <module>/<entities>/<action>.html, each part in lower case with hyphens. The Pages module's show controller renders pages/pages/show.html, the blog post controller blogs/posts/show.html, and the product description controller products/product-descriptions/show.html. There is no frontend/ in the path, although the controllers live in a Frontend folder of the platform: templates/pages/frontend/pages/show.html is never looked for.
The file you wrote is not the one requested, the request reaches the end of the chain, and what you see is the missing-template behaviour below, not an error that mentions the extra segment. If a template you can see on disk never renders, compare its path letter by letter with the name the layout or controller asks for.
The platform has its own exceptions: the OAuth sign-in, consent and passkey screens are named explicitly as o-auth/frontend/…, so those few overrides do include the segment. Copy the platform's path exactly and you cannot get this wrong.
What a missing template looks like
Smarty
Twig
The template a record or controller asked for
The page renders, and its whole body is the text Template not found: pages/pages/show.html
An error: Unable to find template "pages/pages/show.html" (looked into: …), listing the three folders
A file named in an include or extends
An error: Unable to load template 'file:components/card.html'
The same Unable to find template error
An error shows the stack trace on a development environment and the site's error page on production (where errors show). The Twig message is the more useful of the two: it lists the folders searched, so a wrong path is visible at once.
Moving or renaming a template
A layout names its template as a plain path, with nothing that follows the file if it moves. Rename or move a file that a layout points at, and every record on that layout reaches the end of the chain. Check which layouts use a template before you move it, and change the layout's template in the same deploy.