Skip to main content

Type-safe, composable UI components for Python — server-side rendered, HTMX-first, 20+ pre-built components.

Project description

PyPI version Python 3.11+ License: MIT + Commons Clause mypy strict CI Docs

htmforge

Type-safe, composable UI components for Python. Server-side rendered, HTMX-first, framework-agnostic.

Why htmforge?

  • Type-safe props via Pydantic v2: props are validated on construction and on assignment.
  • Small Element primitives with safe rendering: Element.to_html() escapes text and maps Python attrs to HTML (e.g. clsclass).
  • First-class HTMX support: typed enums and helpers for hx-* attrs.
  • Framework adapters: to_fastapi(), to_flask(), to_django() on components for easy integration.
  • Auto-error injection: Forms automatically bind validation errors to fields via the Form.errors dict.
  • 20+ pre-built components: Alerts, DataTables, Modals, Forms, Spinners, Tabs, Toasts, and more.
  • Backward compatible: Extend without breaking existing v0.2.x code.

Installation

pip install htmforge

Quickstart (Flask)

Copy-pasteable minimal Flask example using a Page and a DataTable.

from flask import Flask

from htmforge.components.page import Page
from htmforge.components import DataTable
from htmforge.core.element import Element
from htmforge.elements import div, h1

app = Flask(__name__)

class UsersPage(Page):
    users: list[list[str]]

    def _body_content(self) -> list[Element | str | None]:
        return [
            div(
                h1("Users"),
                DataTable(headers=["Name", "Email"], rows=self.users),
            )
        ]

@app.route("/users")
def users():
    rows = [["Ada Lovelace", "ada@example.com"]]
    return UsersPage(title="Users", users=rows).to_flask()

if __name__ == "__main__":
    app.run(debug=True)

Elements

htmforge.elements exposes small factory functions for HTML tags. These map Pythonic attribute names to HTML and escape text safely. Example:

from htmforge.elements import div, span, input

el = div(
    span("Name:"),
    input(type="search", name="q", hx_get="/search", cls="search"),
    cls="form-row",
)
print(el.to_html())

The hx_get argument renders as hx-get, and text is escaped by default to prevent XSS.

Components

Layout & Structure

Component Description Import
Page Abstract full-page component (adds DOCTYPE) from htmforge.components.page import Page

Data Display

Component Description Import
Alert Dismissible info/success/warning/error box from htmforge.components import Alert
Badge Small inline label with variant classes from htmforge.components import Badge
Breadcrumb Ordered nav with aria-current for current item from htmforge.components import Breadcrumb
DataTable Table with dict/list rows, optional HTMX reload, sortable headers from htmforge.components import DataTable, ColumnDef
Pagination Page links + prev/next, supports HTMX targets from htmforge.components import Pagination
Toast Timed notifications with OOB swap support from htmforge.components import Toast

Navigation & Interaction

Component Description Import
Accordion Collapsible sections using <details>/<summary> from htmforge.components import Accordion
Dropdown Trigger button with dropdown menu items from htmforge.components import Dropdown
Modal Trigger button + <dialog> overlay (HTMX body) from htmforge.components import Modal
SearchInput Search input with keyup debounce via HTMX from htmforge.components import SearchInput
Spinner Accessible loading indicator (SM/MD/LG sizes) from htmforge.components import Spinner, SpinnerSize
Tabs Tab navigation with HTMX lazy-load from htmforge.components import Tabs

Forms & Input

Component Description Import
FormField Label + input + optional error block from htmforge.components import FormField, InputType
CheckboxField Single checkbox with label and error display from htmforge.components import CheckboxField
SelectField Dropdown select with typed options from htmforge.components import SelectField
RadioGroup Radio button group with legend and error from htmforge.components import RadioGroup
FormGroup Container for multiple form fields from htmforge.components import FormGroup
Form Full form with auto-error injection and HTMX from htmforge.components import Form

HTMX integration

Typed enums live in htmforge.htmx: HxSwap, HxTrigger, HxTarget, and HxPushUrl. They render the correct attribute values. Example:

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

btn = button(
    "Delete",
    hx_delete="/items/1",
    hx_swap=HxSwap.OUTER_HTML,
    hx_target=HxTarget.CLOSEST_TR,
)

Use hx_keyup_delay(ms) to produce keyup delay:{ms}ms trigger strings for debounced search inputs.

Framework support

FastAPI example:

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

app = FastAPI()

@app.get("/", response_class=HTMLResponse)
def index():
    return UsersPage(title="Home").to_html()

Flask example (adapter shown above uses to_flask()):

from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return UsersPage(title="Home").to_flask()

Django example:

def index(request):
    return UsersPage(title="Home").to_django()

Testing

htmforge is fully tested with comprehensive coverage:

  • Unit tests: 238 tests passing (+ 5 skipped for optional Django dependency)
    • Component tests: Render logic, HTMX attributes, edge cases
    • Element tests: Attribute mapping, escaping, void elements
    • Framework adapters: FastAPI, Flask, Django with auto-skip for missing dependencies
  • Snapshot tests (21): Regression detection via HTML snapshots
    • Auto-created in tests/snapshots/ on first run
    • Compare rendered HTML on subsequent runs
    • Full coverage of all 20+ components
  • Performance benchmarks (5): Ensure rendering stays fast
    • Element rendering: 1000 iterations <1s
    • Nested elements: 1000 iterations <1s
    • DataTable (10 rows): 1000 renders <2s
    • Alert component: 1000 renders <1s
    • render() helper: 1000 calls <1s

Run tests:

pytest                         # All tests
pytest -v                      # Verbose output
mypy htmforge/ --strict        # Type check (strict mode, 22 files)
ruff check htmforge/           # Lint
ruff format --check htmforge/  # Format check

License

This project is licensed under the MIT License with the Commons Clause condition. It is free for personal projects, open source projects, and small businesses (see LICENSE). Organizations with annual revenue or funding over USD 1,000,000 or more than 100 employees require a separate commercial license from the author.

Contributing

See CONTRIBUTING.md and the docs site at https://mondi04.github.io/htmforge/contributing/ for contribution guidelines.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

htmforge-0.3.0.tar.gz (2.7 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

htmforge-0.3.0-py3-none-any.whl (36.4 kB view details)

Uploaded Python 3

File details

Details for the file htmforge-0.3.0.tar.gz.

File metadata

  • Download URL: htmforge-0.3.0.tar.gz
  • Upload date:
  • Size: 2.7 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.0

File hashes

Hashes for htmforge-0.3.0.tar.gz
Algorithm Hash digest
SHA256 6df3c2488b5cb723a50c4a7de4ca218dc63bd7748ebe61069f73925434328d67
MD5 f4e61b4e6de3d7d7c53eb8a2bba88eeb
BLAKE2b-256 8289f50ca2dd0113b6224a02407f5da35b6e9cfaedceb5472a898bfac2c3dd04

See more details on using hashes here.

File details

Details for the file htmforge-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: htmforge-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 36.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.0

File hashes

Hashes for htmforge-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5c3e6c2ea159b8027f8614c738f4a874d215567daa42b4d4811fbd431a5d6632
MD5 0bd5236a0b10d431f784356649208c7f
BLAKE2b-256 31a3402e60493bfff48403fc0abcc507e4f65fc4229fa45b2eb43ac96b3ebc6d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page