Templates
Vyuh templates provide server-side HTML rendering through Minijinja. Templates
are loaded when a site is built and are available through site.templates() or
the Templates route extractor.
Templates are private bundle resources. They live inside registered bundle
asset dirs under templates/**. They are not served as public assets and are
not copied by collect_static.
Overview
The main public pieces are:
SiteConf::templates(TemplateConf)for template environment configuration.- Asset dir
templates/**for bundle-owned template files. Site::templates()and theTemplatesroute extractor for rendering.- Built-in helpers and filters for assets, reverse URLs, date/time formatting, and common display transforms.
TemplateErrorandTemplateFormatErrorfor loading, rendering, and formatting failures.
Minijinja is the only template engine in v0.
Configuration
Use TemplateConf when the application needs explicit environment behavior:
#![allow(unused)]
fn main() {
use vyuh::prelude::*;
use vyuh::templates::{TemplateAutoEscape, TemplateConf, TemplateDateFormats, TemplateUndefined};
let conf = SiteConf::default().templates(TemplateConf {
auto_escape: TemplateAutoEscape::ByExtension,
undefined: TemplateUndefined::Strict,
trim_blocks: true,
lstrip_blocks: true,
keep_trailing_newline: true,
date_formats: TemplateDateFormats {
date: "%d %b %Y".into(),
time: "%H:%M".into(),
datetime: "%d %b %Y, %H:%M".into(),
},
});
}
Defaults:
- autoescape by extension.
- strict undefined values.
- no block trimming or left stripping.
- keep trailing newline.
- date/time patterns use Chrono strftime syntax:
- date:
%Y-%m-%d - time:
%H:%M - datetime:
%Y-%m-%d %H:%M
- date:
SiteConf::timezone(...) controls local date/time formatting.
Template Sources
Templates are loaded from bundle asset dirs under templates/**. The
templates/ prefix is stripped:
assets/templates/dashboard/base.html -> dashboard/base.html
assets/templates/dashboard/login.html -> dashboard/login.html
Non-template files in asset dirs are ignored by the template loader.
Rendering
Render through site.templates():
#![allow(unused)]
fn main() {
let html = site.templates().render(
"hello.html",
&serde_json::json!({ "name": "Vyuh" }),
)?;
}
Or extract Templates in a route:
#![allow(unused)]
fn main() {
use vyuh::prelude::*;
use vyuh::templates::{TemplateError, Templates};
#[bundles::route(path = "/hello")]
async fn hello(templates: Templates) -> Result<Html<String>, TemplateError> {
templates.html("hello.html", &serde_json::json!({ "name": "Vyuh" }))
}
}
Templates exposes:
render(name, context)- render toString.html(name, context)- render toHtml<String>.exists(name)- check if a template is loaded.names()- list loaded template names for diagnostics.
Includes And Inheritance
Includes, imports, macros, and inheritance use the same template names Vyuh
registers at site build time. A template loaded as dashboard/layouts/base.html
is referenced by that exact name:
{% extends "dashboard/layouts/base.html" %}
{% block title %}Dashboard{% endblock %}
{% block content %}
{% include "dashboard/components/flash.html" %}
<h1>Hello {{ user.name }}</h1>
{% endblock %}
Macros use Minijinja’s normal import syntax:
{% from "dashboard/components/forms.html" import field %}
{{ field("email", "Email address") }}
Built-In Helpers
Vyuh registers helpers that are available to every template:
<link rel="stylesheet" href="{{ asset("dashboard/app.css") }}">
<a href="{{ url_for("user_detail", {"id": user.id}) }}">Profile</a>
Generated at {{ now()|format_datetime }}
Helpers:
asset(path)returns/assets/<path>.url_for(name, params={})reverses a named route and fails rendering if the route cannot be resolved.now()returns the current UTC datetime.
Common filters:
slugifyfilesizeformatlinebreaksbrtruncatechars
Date And Time Formatting
Date/time helpers use SiteConf::timezone(...) and
TemplateConf::date_formats:
{{ post.published_at|format_datetime }}
{{ post.published_at|format_datetime("%d %b %Y, %H:%M") }}
{{ post.published_at|datetime }}
{{ post.published_at|format_date }}
{{ post.published_at|date }}
{{ post.published_at|format_time }}
{{ localdate() }}
{{ localdatetime() }}
The same formatting path is available from Rust:
#![allow(unused)]
fn main() {
let label = vyuh::templates::format_datetime(&site, created_at, None)?;
let custom = vyuh::templates::format_date(&site, created_at, Some("%d %b %Y"))?;
let today = vyuh::templates::localdate::<chrono::DateTime<chrono::Utc>>(&site, None)?;
}
Invalid or unsupported values return TemplateFormatError in Rust and a
Minijinja render error in templates.
Assets Boundary
Templates and public assets share asset dirs but not visibility:
templates/**is private and loaded into Minijinja.public/**is public and served or collected as an asset.
Use public asset URLs from templates:
<link rel="stylesheet" href="{{ asset("dashboard/dashboard.css") }}">
See Assets for public asset serving and collect_static.
Naming And Duplicates
Template names are explicit paths such as dashboard/base.html. There are no
package names or hidden namespace rules.
Duplicate template names fail site build. This keeps rendering deterministic and prevents one bundle from silently replacing another bundle’s template.
Examples
templates_project.rs: project template directory configuration.templates_assets.rs: templates shipped through#[bundles::asset_dir].templates_route.rs: route extraction ofTemplates.templates_config.rs: environment configuration.templates_datetime.rs: date/time formatting configuration and Rust utilities.
Failure Modes
- Missing templates return
TemplateError::NotFound. - Duplicate template names fail during site build.
- Invalid UTF-8 template files fail during site build.
- Invalid template syntax fails during site build.
- Render-time template errors return
TemplateError::RenderError. - Date/time formatting failures return
TemplateFormatErroror render errors.
Current Limitations
- Minijinja is the only supported engine.
- Template filters and globals are framework-provided in v0; arbitrary custom filter/global registration is not yet a stable public API.
- Templates are loaded at site build time; dynamic template reloading is not a public runtime feature.