DEV Community

Mondi
Mondi

Posted on

Type-safe HTML in Python — no templates, no runtime surprises

Type-safe HTML in Python — no templates, no runtime surprises

Been building FastAPI apps for a while.
Every time the same thing: Jinja2 works until it doesn't.

Misspelled variable? Silent empty cell. Missing context key? Crash on render.
No mypy, no autocomplete, no safety net.

I wanted something different, so I built htmforge —
HTML entirely in Python, validated by Pydantic v2.


Before

{# users.html #}
{% for user in users %}
  <td>{{ user.naem }}</td>  {# typo — silent empty cell #}
  <td>{{ user.role }}</td>  {# wrong variant? find out at runtime #}
{% endfor %}
Enter fullscreen mode Exit fullscreen mode

After

from htmforge.components import DataTable, ColumnDef, Badge, BadgeVariant

DataTable(
    columns=[
        ColumnDef(key="name", label="Name"),
        ColumnDef(key="role", label="Role"),
    ],
    dict_rows=[
        {
            "name": user["name"],
            "role": Badge(
                text=user["role"].title(),
                variant=BadgeVariant.DANGER
                if user["role"] == "admin"
                else BadgeVariant.SUCCESS,
            ),
        }
        for user in users
    ],
)
Enter fullscreen mode Exit fullscreen mode

Typo in "name"? Pydantic catches it on startup.
Wrong BadgeVariant? mypy catches it.
XSS? Escaped automatically — no | safe to misuse.


HTMX is typed too

from htmforge.htmx import HxSwap, HxTarget
from htmforge.elements import button

button(
    "Delete",
    hx_delete=f"/users/{user_id}",
    hx_swap=HxSwap.OUTER_HTML,
    hx_target=HxTarget.CLOSEST_TR,
)
Enter fullscreen mode Exit fullscreen mode

No more "outerHTML" string guessing. Full autocomplete.


Stack

  • Pydantic v2 — prop validation on construction and assignment
  • MarkupSafe — XSS protection, automatic, no opt-out
  • 20+ components — DataTable, Form, Modal, Toast, Tabs, Accordion...
  • Framework adapters — FastAPI, Flask, Django built in
  • 240 tests, mypy strict, ruff clean

Looking for contributors

The project is early but functional. Good first issues are documented
in CONTRIBUTING.md — from small things like adding a lang attribute
to Page, to rendering improvements in DataTable.

If this approach interests you:

Top comments (0)