Templates¶
Setup Templates Directory¶
Configure the templates directory when creating your app:
Create a Template¶
Create a .gmi file in your templates directory:
{# templates/page.gmi #}
# {{ title }}
{{ description }}
{% for item in items %}
* {{ item }}
{% endfor %}
=> / Home
Render a Template¶
Use app.template() in your handler:
@app.gemini("/page")
def page(request: Request):
return app.template(
"page.gmi",
title="My Page",
description="Welcome to my page!",
items=["Item 1", "Item 2", "Item 3"]
)
Gemtext Filters¶
Xitzin provides filters for generating Gemtext:
link - Generate Links¶
{{ "/about" | link("About Us") }}
{# Output: => /about About Us #}
{{ "/home" | link }}
{# Output: => /home #}
reverse - URL Reversing¶
The reverse() function builds URLs from route names:
{# Link to a named route #}
{{ reverse("user_profile", username="alice") | link("Alice's Profile") }}
{# Output: => /user/alice Alice's Profile #}
{# Multiple parameters #}
{{ reverse("blog_post", year=2024, month=12, slug="hello") }}
{# Output: /post/2024/12/hello #}
This keeps your templates maintainable - if a route path changes, only the route definition needs updating.
heading - Generate Headings¶
{{ "Main Title" | heading(1) }}
{# Output: # Main Title #}
{{ "Section" | heading(2) }}
{# Output: ## Section #}
{{ "Subsection" | heading(3) }}
{# Output: ### Subsection #}
list - Generate Lists¶
{{ items | list }}
{# Given items = ["Apple", "Banana", "Cherry"]
Output:
* Apple
* Banana
* Cherry
#}
quote - Generate Quotes¶
{{ "This is quoted text" | quote }}
{# Output: > This is quoted text #}
{{ multiline_text | quote }}
{# Handles newlines, prefixing each line with > #}
preformat - Code Blocks¶
code here}¶
## Template Inheritance
Create a base template:
```jinja
{# templates/base.gmi #}
# {{ title }}
{% block content %}{% endblock %}
---
{{ "/" | link("Home") }}
Extend it in other templates:
{# templates/about.gmi #}
{% extends "base.gmi" %}
{% block content %}
Welcome to the about page!
This is my personal capsule.
{% endblock %}
Conditional Content¶
{% if user %}
Welcome back, {{ user.name }}!
{% else %}
{{ "/login" | link("Sign In") }}
{% endif %}
Loops¶
## Articles
{% for article in articles %}
{{ article.url | link(article.title) }}
{% endfor %}
{% if not articles %}
No articles yet.
{% endif %}
Template with Request Context¶
Pass request data to templates:
@app.gemini("/profile")
@optional_certificate
def profile(request: Request):
identity = request.state.identity
return app.template(
"profile.gmi",
identity=identity,
is_authenticated=identity is not None
)
Inline Templates¶
For simple cases, use render_string:
from xitzin.templating import TemplateEngine
engine = TemplateEngine(Path("templates"))
content = engine.render_string(
"# {{ title }}\n\n{{ items | list }}",
title="Quick Page",
items=["A", "B", "C"]
)
Dynamic Navigation with reverse()¶
Build navigation menus that don't break when URLs change:
{# templates/base.gmi #}
# {{ title }}
{% block content %}{% endblock %}
---
## Navigation
{{ reverse("home") | link("Home") }}
{{ reverse("about") | link("About") }}
{% if user %}
{{ reverse("user_profile", username=user.username) | link("My Profile") }}
{{ reverse("logout") | link("Logout") }}
{% else %}
{{ reverse("login") | link("Login") }}
{% endif %}
Best Practices¶
- Use templates for complex pages - Keep handlers clean
- Use filters for Gemtext - They handle formatting correctly
- Create a base template - For consistent navigation
- Pass minimal context - Only what the template needs
- Use reverse() for links - Avoid hardcoded URLs