HTMX-style declarative UI bindings for PyWebview apps
Project description
pywebview-htmx
pywebview-htmx (import as pywebview_htmx) brings HTMX-style declarative interactions to PyWebview apps.
You write mostly normal HTML, annotate interactive elements with py-* attributes,
and pywebview-htmx wires those elements to Python methods exposed through
window.pywebview.api.
What You Get
- Declarative Python calls from HTML (
py-call,py-trigger,data-py-params) - Declarative target updates (
py-target,py-swap) - Built-in loading state support (
py-wait+.py-waiting) - Event hooks for lifecycle instrumentation (
py:trigger,py:beforeSwap, etc.) - Concurrency control (
latest-winsordrop) without custom plumbing - Automatic processing of newly swapped HTML fragments
- Built-in theme system (multiple shipped themes + reusable component CSS)
Mental Model
pywebview-htmx is a tiny runtime that does four core jobs:
- Find elements with
py-call. - Bind an event listener (default:
click, override withpy-trigger). - Call your Python API method through
window.pywebview.api[method](params). - Swap returned HTML into the DOM according to
py-target+py-swap.
In other words: your Python methods are your "server handlers", and returned HTML is your "response body".
Installation
In this repository
pdm install
In another PDM project
pdm add pywebview-htmx
Quick Start
from pywebview_htmx import create_window
class API:
def greeting(self, params: dict) -> str:
name = params.get("name", "world")
return f"<p>Hello, {name}!</p>"
html = """
<!doctype html>
<html>
<body>
<button
py-call="greeting"
py-target="#result"
data-py-params='{"name": "pywebview-htmx"}'>
Say hello
</button>
<div id="result"></div>
</body>
</html>
"""
create_window("Quickstart", html, js_api=API())
You do not manually include runtime.js when using create_window();
it is injected automatically.
API Reference (Python)
create_window(title, html, js_api=None, theme="aurora", start=True, **kwargs)
Creates a PyWebview window with injected theme CSS (optional) and injected
pywebview-htmx runtime script.
title: window titlehtml: HTML document stringjs_api: Python object exposed aswindow.pywebview.apitheme: bundled theme name ("aurora","paper","cybermind") orNonestart: whether to callwebview.start()automatically**kwargs: forwarded towebview.create_window()
When to set start=False:
- You need to create multiple windows before starting
- You want to manage PyWebview startup/lifecycle yourself
get_runtime_script()
Returns the bundled JavaScript runtime as a string.
inject_runtime(html)
Injects the runtime script tag into an HTML string (idempotent).
Theme helpers
list_themes()-> sorted list of available themesget_theme_css(theme)-> base CSS + selected theme CSSinject_theme(html, theme)-> inject or replace theme<style>block
Constants
DEFAULT_THEME(currently"aurora")
Declarative Attribute Reference
py-call (required)
Python API method name to invoke.
<button py-call="fetch_user">...</button>
py-trigger (optional)
DOM event name to bind. Default is click.
<form py-call="submit_form" py-trigger="submit">...</form>
<div py-call="show_tip" py-trigger="mouseenter">...</div>
data-py-params (optional)
JSON payload passed to Python method.
<button data-py-params='{"user_id": 42, "mode": "full"}'>...</button>
Notes:
- If missing, params default to
{}. - Invalid JSON logs an error and falls back to
{}.
py-target (optional)
CSS selector of the element to update. If omitted, the triggering element is updated.
py-swap (optional)
How returned HTML is applied:
innerHTML(default)outerHTMLappend
Unknown value falls back to innerHTML.
py-wait (optional)
CSS selector of element receiving .py-waiting while request is in flight.
If missing/empty/unresolvable, the triggering element is used.
Runtime Config (JavaScript)
window.pywebviewHtmx.config includes:
defaultSwapStyle(default:"innerHTML")swapDelay(ms, default:0)settleDelay(ms, default:20)requestPolicy("latest-wins"default, or"drop")
Example:
window.pywebviewHtmx.config.requestPolicy = "drop";
window.pywebviewHtmx.config.swapDelay = 150;
window.pywebviewHtmx.config.settleDelay = 50;
Lifecycle Events
pywebview-htmx dispatches custom events you can observe for telemetry, debugging, and UX:
py:triggerpy:beforeSwappy:afterSwappy:ignored(whenrequestPolicy="drop"and request is in flight)py:error
Example:
document.body.addEventListener("py:error", (event) => {
console.error("pywebview-htmx error", event.detail.error);
});
Concurrency Behavior
Per trigger element, pywebview-htmx tracks request state.
latest-wins (default)
Multiple rapid requests are allowed; stale responses are ignored. Only latest request updates the DOM.
drop
If a request is already in flight for that element, new triggers are ignored and
py:ignored is emitted.
Dynamic Content Re-processing
After a swap, pywebview-htmx automatically scans swapped content for new py-call
elements and binds them. This enables chained interactions in returned fragments
without manual re-init code.
Theme System
pywebview-htmx ships a reusable component styling system plus multiple themes.
from pywebview_htmx import create_window, list_themes
print(list_themes())
# ['aurora', 'cybermind', 'paper']
create_window("My App", html, js_api=api, theme="cybermind")
Set theme=None to disable automatic theme injection.
Built-in Component Classes
The bundled CSS supports both pyh-* classes and compatibility aliases used by
the demo (.hero, .demo-card, .btn, etc.).
Recommended canonical classes:
- Layout:
.pyh-shell,.pyh-hero,.pyh-grid,.pyh-card - Controls:
.pyh-btn,.pyh-btn-primary,.pyh-btn-secondary,.pyh-btn-ghost,.pyh-btn-danger - Content:
.pyh-result,.pyh-note,.pyh-code,.pyh-chip,.pyh-muted - Logs/lists:
.pyh-activity-list,.pyh-event-log - Utilities:
.pyh-row,.pyh-full-width,.pyh-inline-config
Shipping Your Own Theme
Use the same token pattern as bundled themes (--pyh-* variables) and inject your
custom CSS before runtime initialization. If you want PyWebview HTMX-style replacement
behavior, add your own <style data-pywebview-theme="my-theme">...</style> block.
Practical Patterns
Pattern 1: Form submit to Python
- Use
py-trigger="submit"on<form> - Keep
data-py-paramsin sync from form inputs (via JS) py-targeta result card/summary region
Pattern 2: Append activity/log rows
- Use
py-swap="append"to add<li>rows - Return a single row snippet from Python
Pattern 3: Replace full component
- Use
py-swap="outerHTML" - Return full replacement markup with same outer id/selector
Pattern 4: Theme switch from Python
- Expose a
switch_themePython method - Return HTML containing
<style data-pywebview-theme="...">...</style> - Swap a wrapper section with
py-swap="outerHTML"
Security Notes
- Returned HTML is inserted directly into the DOM.
- Escape/sanitize untrusted data before returning markup.
- Treat Python API methods as privileged application logic.
Troubleshooting
"Nothing happens when I click"
Check:
py-callmatches an existing method onwindow.pywebview.api- method returns a string of HTML
- no JSON parse error in
data-py-params py-targetselector exists
"New buttons in swapped HTML do not work"
This should work by default via post-swap processing. If you perform manual DOM changes outside of pywebview-htmx swaps, call:
window.pywebviewHtmx.process(document.body);
"Loading state looks wrong"
Style .py-waiting globally and/or point py-wait at a dedicated element.
Demo
Run the full feature showcase:
pdm run python app.py
The demo includes:
- Runtime config controls
- Live event feed
- All swap modes
- Multiple trigger types
- Concurrency policy behavior
- Dynamic fragment processing
- Python-driven live theme switching
License
MIT (see LICENSE).
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
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 pywebview_htmx-0.2.1.tar.gz.
File metadata
- Download URL: pywebview_htmx-0.2.1.tar.gz
- Upload date:
- Size: 19.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
acc7ab9414c13d6e6242b5b43577857344497d61b3a7ba307db7effdcd8905a9
|
|
| MD5 |
d76af7abff09bccfbf7c08d1296ef1dd
|
|
| BLAKE2b-256 |
2188379ffb199b64fa5f5e84a9f9e9f551c71d87320ae35124105b8c57ebe5fb
|
Provenance
The following attestation bundles were made for pywebview_htmx-0.2.1.tar.gz:
Publisher:
python-publish.yml on btfranklin/pywebview-htmx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pywebview_htmx-0.2.1.tar.gz -
Subject digest:
acc7ab9414c13d6e6242b5b43577857344497d61b3a7ba307db7effdcd8905a9 - Sigstore transparency entry: 1058320601
- Sigstore integration time:
-
Permalink:
btfranklin/pywebview-htmx@264568be1c613301e2b7a49f64a7117b6a750f36 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/btfranklin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@264568be1c613301e2b7a49f64a7117b6a750f36 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pywebview_htmx-0.2.1-py3-none-any.whl.
File metadata
- Download URL: pywebview_htmx-0.2.1-py3-none-any.whl
- Upload date:
- Size: 15.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
32debfbd7fb85e852224a1865c4c07284102afe13991fc547e4cf636058123b3
|
|
| MD5 |
b91eea060a34f9b185543486752d2102
|
|
| BLAKE2b-256 |
f6c4c909db236ca10065c9d32418ecf5fdaf270af8968dacc8f9fe97cfb570e7
|
Provenance
The following attestation bundles were made for pywebview_htmx-0.2.1-py3-none-any.whl:
Publisher:
python-publish.yml on btfranklin/pywebview-htmx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pywebview_htmx-0.2.1-py3-none-any.whl -
Subject digest:
32debfbd7fb85e852224a1865c4c07284102afe13991fc547e4cf636058123b3 - Sigstore transparency entry: 1058320606
- Sigstore integration time:
-
Permalink:
btfranklin/pywebview-htmx@264568be1c613301e2b7a49f64a7117b6a750f36 -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/btfranklin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@264568be1c613301e2b7a49f64a7117b6a750f36 -
Trigger Event:
release
-
Statement type: