# Templates **Render HTML templates using Jinja2.** - [Overview](#overview) - [Template files](#template-files) - [Template context](#template-context) - [Built-in globals](#built-in-globals) - [Built-in filters](#built-in-filters) - [Custom globals and filters](#custom-globals-and-filters) - [Custom template extensions](#custom-template-extensions) - [Rendering templates manually](#rendering-templates-manually) - [Custom Jinja environment](#custom-jinja-environment) - [FAQs](#faqs) - [Installation](#installation) ## Overview Plain uses Jinja2 for template rendering. You can refer to the [Jinja documentation](https://jinja.palletsprojects.com/en/stable/) for all of the features available. Templates are typically used with `TemplateView` or one of its subclasses. ```python # app/views.py from plain.views import TemplateView class ExampleView(TemplateView): template_name = "example.html" def get_template_context(self): context = super().get_template_context() context["message"] = "Hello, world!" return context ``` ```html {% extends "base.html" %} {% block content %}
Generated at {{ now() }}
``` ## Built-in filters Plain includes several [filters](./jinja/filters.py) for common operations: | Filter | Description | | ----------------------------- | ------------------------------------ | | `strftime(format)` | Formats a datetime | | `strptime(format)` | Parses a string to datetime | | `fromtimestamp(ts)` | Creates datetime from timestamp | | `fromisoformat(s)` | Creates datetime from ISO string | | `localtime(tz)` | Converts to local timezone | | `timeuntil` | Human-readable time until a date | | `timesince` | Human-readable time since a date | | `json_script(id)` | Outputs JSON safely in a script tag | | `islice(stop)` | Slices iterables (useful for dicts) | | `pluralize(singular, plural)` | Returns plural suffix based on count | ```htmlPosted {{ post.created_at|timesince }} ago
{{ items|length }} item{{ items|length|pluralize }}
{{ 5 }} ox{{ 5|pluralize("en") }}
{{ data|json_script("page-data") }} ``` ## Custom globals and filters You can register your own globals and filters in `app/templates.py` (or `{package}/templates.py`). These files are automatically imported when the template environment loads. ```python # app/templates.py from plain.templates import register_template_filter, register_template_global @register_template_filter def camel_case(value): """Convert a string to CamelCase.""" return value.replace("_", " ").title().replace(" ", "") @register_template_global def app_version(): """Return the current app version.""" return "1.0.0" ``` Now you can use these in templates: ```html{{ "my_variable"|camel_case }}
``` You can also register non-callable values as globals by providing a name: ```python from plain.templates import register_template_global register_template_global("1.0.0", name="APP_VERSION") ``` ## Custom template extensions For more complex template features, you can create Jinja extensions. The [`InclusionTagExtension`](./jinja/extensions.py#InclusionTagExtension) base class makes it easy to create custom tags that render their own templates. ```python # app/templates.py from plain.templates import register_template_extension from plain.templates.jinja.extensions import InclusionTagExtension from plain.runtime import settings @register_template_extension class AlertExtension(InclusionTagExtension): tags = {"alert"} template_name = "components/alert.html" def get_context(self, context, *args, **kwargs): return { "message": args[0] if args else "", "type": kwargs.get("type", "info"), } ``` ```html{{ item.name }}
{% endfor %} ``` #### Where can I learn more about Jinja2? The [Jinja2 documentation](https://jinja.palletsprojects.com/en/stable/) covers all the template syntax, including conditionals, loops, macros, and inheritance. ## Installation The `plain.templates` module is included with Plain by default. No additional installation is required.