Built-in Filters
Chirp ships with built-in filters that are automatically available in every template. These complement Kida's core filters with web-specific utilities.
field_errors
Extract validation errors for a single form field from an errors dict. Returns a list of error messages.
{% for msg in errors | field_errors("email") %}
<p class="error">{{ msg }}</p>
{% end %}
If the field has no errors, or errors is None, an empty list is returned — so the loop simply produces nothing.
Typical usage withform_or_errors():
@app.route("/signup", methods=["POST"])
async def signup(request: Request):
result = await form_or_errors(request, SignupForm, "signup.html", "form")
if isinstance(result, ValidationError):
return result # errors and form values are included
# ... process valid data
<label>Email</label>
<input name="email" value="{{ form.email ?? "" }}">
{% for msg in errors | field_errors("email") %}
<span class="field-error">{{ msg }}</span>
{% end %}
qs
Build a URL with query-string parameters. Falsy values are automatically omitted.
<a href="{{ '/search' | qs(q=query, page=page) }}">Search</a>
{# Output: /search?q=hello&page=2 #}
Falsy values (None, "", 0, False) are dropped:
{{ '/items' | qs(q=query, category=category, page=none) }}
{# If category is "", outputs: /items?q=hello #}
Appends to existing query strings:
{{ '/search?q=hello' | qs(page=2) }}
{# Output: /search?q=hello&page=2 #}
Special characters are URL-encoded:
{{ '/search' | qs(q="hello world") }}
{# Output: /search?q=hello%20world #}
Custom Filters
Filters transform values in templates. Register them with@app.template_filter():
@app.template_filter()
def currency(value: float) -> str:
return f"${value:,.2f}"
@app.template_filter()
def pluralize(count: int, singular: str, plural: str) -> str:
return singular if count == 1 else plural
Use them in templates with the pipe syntax:
<span class="price">{{ product.price | currency }}</span>
<span>{{ count }} {{ count | pluralize("item", "items") }}</span>
Named Filters
By default, the function name becomes the filter name. Override it with an argument:
@app.template_filter("fmt_date")
def format_date(dt: datetime) -> str:
return dt.strftime("%B %d, %Y")
<time>{{ post.created_at | fmt_date }}</time>
Template Globals
Globals are functions or values available in every template without being passed in the context:
@app.template_global()
def site_name() -> str:
return "My App"
@app.template_global()
def current_year() -> int:
return datetime.now().year
<footer>© {{ current_year() }} {{ site_name() }}</footer>
Registration Timing
Filters and globals must be registered during the setup phase (beforeapp.run()or the first request). They become part of the kida environment at freeze time.
app = App()
# Register during setup
@app.template_filter()
def upper(value: str) -> str:
return value.upper()
# This works
app.run()
# Registering after freeze would raise an error
Type Safety
Filters are regular Python functions with full type annotations. Your IDE provides autocomplete and type checking for filter arguments.
@app.template_filter()
def truncate(value: str, length: int = 50, suffix: str = "...") -> str:
if len(value) <= length:
return value
return value[:length].rsplit(" ", 1)[0] + suffix
Next Steps
- Rendering -- How templates are rendered
- App Lifecycle -- When filters are registered
- API Reference -- Complete API surface