Concepts
Templates and shortcodes
How a page is assembled from small templates and shortcodes, and how templates are found.
Templates use shortcodes enclosed in [# and #].
[#shortcode#]
[#shortcode param#]
[#shortcode key=value key2=value2#]
Shortcodes can be nested - any parameter value or shortcode name can itself contain a shortcode:
[#get data.users.[#get selected-id#]#]
This also works inside tpl= parameters and shortcode names, which lets you compose template names dynamically:
[#if current_image=(not-empty) tpl=card-image[#if current_image=(empty) echo=-placeholder#]#]
Resolves to tpl=card-image when an image exists, tpl=card-image-placeholder when it doesn't.
A particularly powerful application is content-type dispatch - embed a type field directly in the shortcode name to route to a type-specific template without any branching:
[#card-[#get item.type#]#]
Resolves to [#card-news#], [#card-event#], [#card-download#], etc. based on the item's type. This pattern eliminates chains of [#if item.type=X tpl=Y#] conditions and scales cleanly as new types are added.
Use default= to guarantee a valid shortcode name when the value may be missing or unrecognised - an unresolved shortcode renders as literal text in the output:
[#card-[#get item.type default=unknown#]#]
Implement [#card-unknown#] as a generic fallback (or leave it empty to render nothing).
Templates are plain files in the target output format (HTML, JSON, CSS, etc.). They have a .tpl extension.
Route templates
Every URL maps to a file in ext/uri/:
ext/uri/index.tpl → /
ext/uri/about/index.tpl → /about/
ext/uri/blog/(slug)/index.tpl → /blog/<anything>/
A route template renders the page:
[#html#]
The main.tpl in the same folder contains the page body, rendered inside the HTML shell. It is always scoped to that route - even ext/uri/main.tpl is only the body for the home page (/), not a global layout.
Reusable templates
Stored in ext/tpl/<name>/index.tpl. Called with their name as a shortcode:
[#hero-section#]
Template components do not receive parameters directly. To pass data to a template, use [#set ... overwrite#] before calling it:
[#set back_url=/agenda overwrite#]
[#back-button#]
Inside the template, read the variable with [#get back_url default=/#]. Lib shortcodes (ext/lib/<name>/) are different - they receive $params and can be called with inline parameters.
URI-scoped templates
Templates can also live flat inside a URI folder (not in a subfolder). These are not routes - they are partial templates included from within that route's index.tpl or main.tpl.
ext/uri/new/page-settings-fields.tpl ← partial, not a route
ext/uri/new/index.tpl ← route
ext/uri/new/main.tpl ← route body
Important: a subfolder inside ext/uri/ always creates a new route. A flat .tpl file in a URI folder is a partial template scoped to that route.