Rendering Contexts

Passing variables and context to templates

2 min read 469 words

Pass data to templates through the rendering context.

Basic Rendering

Pass variables as keyword arguments:

template = env.get_template("page.html")
html = template.render(
    title="My Page",
    user=current_user,
    items=item_list,
)

Or as a dictionary:

context = {
    "title": "My Page",
    "user": current_user,
    "items": item_list,
}
html = template.render(context)

Convenience Methods

Environment provides shortcuts:

# Combines get_template() + render()
html = env.render("page.html", title="Hello")

# Combines from_string() + render()
html = env.render_string("{{ x * 2 }}", x=21)

Global Variables

Variables available in all templates:

env = Environment(loader=FileSystemLoader("templates/"))

# Add globals
env.add_global("site_name", "My Site")
env.add_global("current_year", 2024)
env.add_global("format_date", format_date_func)

Access in templates:

<title>{{ site_name }}</title>
<footer>&copy; {{ current_year }}</footer>
{{ format_date(post.date) }}

Built-in Globals

Kida includes common Python builtins:

{{ range(10) }}
{{ len(items) }}
{{ dict(a=1, b=2) }}
{{ max(scores) }}

Available:range,dict,list,set,tuple,len,str,int,float,bool,abs,min,max,sum,sorted,reversed,enumerate,zip,map,filter.

Object Access

Templates access object attributes:

class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email

template.render(user=User("Alice", "alice@example.com"))
{{ user.name }}
{{ user.email }}

Dictionary Access

template.render(config={"timeout": 30, "retries": 3})
{{ config.timeout }}
{{ config["timeout"] }}

Nested Contexts

Build complex nested data:

template.render(
    site={
        "title": "My Site",
        "nav": [
            {"title": "Home", "url": "/"},
            {"title": "About", "url": "/about"},
        ],
    },
    page={
        "title": "Welcome",
        "content": "Hello, world!",
    },
)
<title>{{ page.title }} - {{ site.title }}</title>

<nav>
{% for item in site.nav %}
    <a href="{{ item.url }}">{{ item.title }}</a>
{% end %}
</nav>

Undefined Variables

By default, undefined variables raise errors:

template = env.from_string("{{ missing }}")
template.render()  # Raises UndefinedError

Usedefaultfilter for optional values:

{{ user.nickname | default("Anonymous") }}
{{ config.timeout | default(30) }}

Context Isolation

Eachrender()call starts with a fresh context:

# These don't affect each other
html1 = template.render(x=1)
html2 = template.render(x=2)

Globals are shared but render context is isolated.

Best Practices

Keep Context Flat

# ✅ Flat, easy to access
template.render(
    title=page.title,
    user=current_user,
    items=items,
)

# ❌ Deeply nested
template.render(
    data={
        "page": {"meta": {"title": ...}},
        ...
    }
)

Use Typed Objects

# ✅ IDE support, validation
@dataclass
class PageContext:
    title: str
    user: User
    items: list[Item]

template.render(**asdict(PageContext(...)))

Precompute in Python

# ✅ Python handles complexity
template.render(
    formatted_items=[format_item(i) for i in items],
    total=sum(i.price for i in items),
)

# ❌ Complex logic in template
# {% set total = 0 %}
# {% for item in items %}{% set total = total + item.price %}{% end %}

See Also