UI components for Python using Pydantic and Jinja2 templates
Project description
PyJinHx
Build reusable, type-safe UI components for template-based web apps in Python. PyJinHx combines Pydantic models with Jinja2 templates to give you template discovery, component composition, and JavaScript bundling.
- Automatic Template Discovery: Place templates next to component files—no manual paths
- JavaScript Bundling: Automatically collects and bundles
.jsfiles from component directories - Composability: Nest components easily—works with single components, lists, and dictionaries
- Flexible: Use Python classes for reusable components, HTML syntax for quick page composition
- Type Safety: Pydantic models provide validation and IDE support
Installation
pip install pyjinhx
Example
This single example shows the full setup (Python classes + templates) and both ways to render:
- Python-side: instantiate a component class and call
.render(). - Template-side: render an HTML-like string with custom tags via
Renderer.
Step 1: Define component classes
# components/ui/button.py
from pyjinhx import BaseComponent
class Button(BaseComponent):
id: str
text: str
variant: str = "default"
# components/ui/card.py
from pyjinhx import BaseComponent
from components.ui.button import Button
class Card(BaseComponent):
id: str
title: str
action_button: Button
# components/ui/page.py
from pyjinhx import BaseComponent
from components.ui.card import Card
class Page(BaseComponent):
id: str
title: str
main_card: Card
Step 2: Create templates (auto-discovered next to the class files)
<!-- components/ui/button.html -->
<button id="{{ id }}" class="btn btn-{{ variant }}">{{ text }}</button>
<!-- components/ui/card.html -->
<div id="{{ id }}" class="card">
<h2>{{ title }}</h2>
<div class="action">{{ action_button }}</div>
</div>
<!-- components/ui/page.html -->
<main id="{{ id }}">
<h1>{{ title }}</h1>
{{ main_card }}
</main>
Step 3A: Python-side rendering (BaseComponent.render())
from components.ui.button import Button
from components.ui.card import Card
from components.ui.page import Page
page = Page(
id="home",
title="Welcome",
main_card=Card(
id="hero",
title="Get Started",
action_button=Button(id="cta", text="Sign up", variant="primary"),
),
)
html = page.render()
Step 3B: Template-side rendering (Renderer(...).render(source))
from pyjinhx import Renderer
renderer = Renderer("./components", auto_id=True)
html = renderer.render(
"""
<Page title="Welcome">
<Card title="Get Started">
<Button text="Sign up" variant="primary"/>
</Card>
</Page>
"""
)
Template-side rendering supports:
- Type safety for registered classes: if
Button(BaseComponent)exists, its fields are validated when<Button .../>is instantiated. - Generic tags: if there is no registered class, a generic
BaseComponentis used as long as the template file can be found.
JavaScript & extra assets
- Component-local JS: if a component class
MyWidgethas a sibling filemy-widget.js, it is auto-collected and injected once at the root render level. - Extra JS: pass
js=[...]with file paths; missing files are ignored. - Extra HTML files: pass
html=[...]with file paths; they are rendered and exposed in the template context by filename stem (e.g.extra_content.html→extra_content.htmlwrapper). Missing files raiseFileNotFoundError.
FastAPI + HTMX example
Component class
# components/ui/button.py
from pyjinhx import BaseComponent
class Button(BaseComponent):
id: str
text: str
Component template (with HTMX)
<!-- components/ui/button.html -->
<button
id="{{ id }}"
hx-post="/clicked"
hx-vals='{"button_id": "{{ id }}"}'
hx-target="#click-result"
hx-swap="innerHTML"
>
{{ text }}
</button>
FastAPI app (two routes)
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from components.ui.button import Button
app = FastAPI()
@app.get("/button", response_class=HTMLResponse)
def button() -> str:
return (
Button(id="save-btn", text="Click me").render()
+ '<div id="click-result"></div>'
)
@app.post("/clicked", response_class=HTMLResponse)
def clicked(button_id: str = "unknown") -> str:
return f"Clicked: {button_id}"
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyjinhx-0.2.7.tar.gz.
File metadata
- Download URL: pyjinhx-0.2.7.tar.gz
- Upload date:
- Size: 67.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.25
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4c8dc34e96e906081d4b3a169b9f2fe852fe0960b97bb1c3e98fce09d1244de8
|
|
| MD5 |
8b5c71b6f470e4c636ba758f0bc5d91e
|
|
| BLAKE2b-256 |
d6e93de1d1b818ff33e02ebc8ad533fd1fc1b8262ae54db2f3b18e732932f638
|
File details
Details for the file pyjinhx-0.2.7-py3-none-any.whl.
File metadata
- Download URL: pyjinhx-0.2.7-py3-none-any.whl
- Upload date:
- Size: 16.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.25
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7967c5619434de5924ed035560593831663094eb144918654735c4981c379e67
|
|
| MD5 |
9d09a30f576b55f43e1f5917929069cb
|
|
| BLAKE2b-256 |
dc0aa10dc542590ec724b846cd76a318627d513a14a3c2cd12aa3bab491ee093
|