0.9.0 to 0.10.0
The 0.10.0 release replaces the old layout.html (Python format string) layout support with first-class Jinja layout support. holm now ships its own JinjaTemplate component and registers a Jinja2Templates instance for you when it owns the htmy renderer, so Jinja layouts and templates work with zero configuration.
Summary of changes
layout.htmlfiles are no longer recognized. Rename them tolayout.jinja.- The
str_to_layoutargument ofApp()and theholm.utilsmodule (includingsnippet_to_layout) have been removed. - Default slots are now configured with the
layout_slotsargument ofApp(), not through a custom string-to-layout converter. holmnow exportsJinjaTemplatefor rendering Jinja templates ashtmycomponents anywhere in your application.
Rename and rewrite layouts
For each layout.html, rename the file to layout.jinja and translate the template syntax:
Old (layout.html) |
New (layout.jinja) |
|---|---|
{metadata[title]} |
{{ metadata.title }} |
{request.method} |
{{ request.method }} |
<!-- slot[children] --> |
{{ slots.children }} |
<!-- slot[details] --> |
{{ slots.details }} |
The default slot is still children. The wrapped page or layout is automatically placed in {{ slots.children }} unless it returns a mapping, in which case the mapping keys become the slot names. This behavior did not change.
metadata and request are still available. route_params is newly available and exposes resolved FastAPI dependencies, e.g. {{ route_params.current_user }}.
layout.py continues to take precedence when both layout.py and layout.jinja exist in the same package.
Drop custom str_to_layout
Previously you could plug tools like Jinja2 into HTML layouts by passing str_to_layout to App(). This is gone. Jinja is now the built-in and only string-based layout format.
If your custom converter only translated the template syntax, you can delete it: layout.jinja files are picked up automatically.
If your converter provided default slots (e.g. a navbar on every page) using snippet_to_layout(..., default_slot_mapping=...), switch to layout_slots:
Slots configured this way are injected into the htmy rendering context using htmy.jinja.DefaultSlots and are available in every Jinja layout as {{ slots.navbar }}. Slots returned explicitly by a page or layout take precedence over layout_slots for the same name.
Auto-escaping
layout.html used str.format(), which does no HTML escaping by default.
When holm creates the htmy renderer, it enables Jinja autoescaping for the jinja2.select_autoescape() default extensions (html, htm, xml) plus the .jinja extension.
layout.jinjafiles and any.jinjatemplate rendered viaholm.JinjaTemplateare auto-escaped.- Slots (e.g.
{{ slots.children }}) are pre-rendered byhtmyand are marked as safe, so they are never double-escaped.
If you provide your own htmy renderer, configure your Jinja2Templates with the same autoescape setting (jinja2.select_autoescape(["html", "htm", "xml", "jinja"])) to get the same behavior.
Custom htmy renderer
When holm creates the htmy renderer (the htmy argument to App() is None), it automatically registers a htmy.jinja.JinjaTemplates instance in the default rendering. Jinja layouts and JinjaTemplate components then work out of the box.
If you pass your own htmy to App(), holm does not modify it. You must add a pre-configured htmy.jinja.JinjaTemplates to its default context yourself, and ensure the templates root directory is the Python import root directory (the directory containing your app package), so template names resolve the same way as for holm-owned renderers.
Rendering Jinja anywhere
To render a Jinja template as an htmy component outside a layout, use holm.JinjaTemplate:
from htmy import Component
from holm import JinjaTemplate
def page() -> Component:
return JinjaTemplate(
"my_app/components/card.html",
jinja_context={"title": "Hello"},
)
You can of course keep Jinja templates outside of the app package, for example in a templates directory next to your app package, in which case the template name would be for example templates/card.html.
JinjaTemplate looks up the template source from the htmy rendering context, so it works whenever a JinjaTemplates instance is available.