Skip to content
LoboGuardian 🐺 edited this page Jul 11, 2026 · 6 revisions

πŸƒ Welcome to HTeaLeaf

"Hoja de HyperTe" β€” A Python server-side rendering framework with selective Python-to-JavaScript compilation.

HTeaLeaf is an opinionated Python web framework. It has clear opinions about where state lives (the server), how the DOM updates (server-authoritative reconciliation), and where the Python/JS boundary sits (explicit, in your code).


Why HTeaLeaf?

One language, one codebase.

HTeaLeaf lets you build full web applications in pure Python: components, state, interactivity and all. No context switching, no separate frontend build step, no state synchronization between client and server. When you need JavaScript, it's there: via transpiled Python, raw JS blocks, external files, or CDNs.

  • One rendering model. Server-side rendering with surgical, opt-in client interactivity β€” no hydration mismatch, no bundle bloat by default.
  • Reusable components. Components are plain Python functions. Share them across routes, compose them, pass arguments. it's just Python.
  • One state model. Store for module-level shared state, LocalState / use_state() for frontend state.
  • Explicit Python/JS boundary. The .js namespace on state objects makes the compilation boundary visible in your code. store.update(...) runs at render time (Python). store.js.update(...) compiles to JavaScript and runs in the browser.

Components as plain Python functions make HTeaLeaf code easy to read, write, and generate.


Core Concepts

Rendering

HTeaLeaf renders routes server-side and injects only the JavaScript that your @js-decorated functions require. There is no default client-side framework β€” the browser receives HTML with targeted interactivity attached.

State

Primitive Scope Lives in
Store() Module-level, shared across requests Module global
use_state() / LocalState Route-scoped, per-render Handler function

The @js Decorator

The @js decorator marks a Python function for transpilation to JavaScript. HTeaLeaf's AST transformer rewrites Python variable names to their stable JS identifiers before compilation, so you write Python and get correct JS.

from htealeaf import use_state
from htealeaf.js import js

@app.route("/")
async def index():
    counter = use_state(0)  # must be created inside the handler

    @js
    def increment():
        counter.set(counter.get() + 1)
    ...

The .js Namespace

State objects expose a .js property returning a JSCode. This makes the execution boundary explicit:

# Runs server-side at render time:
store.update(new_value)
 
# Compiles to JavaScript, runs in the browser:
store.js.update(new_value)
store.js.delete()

Auto-injection

RenderContext (backed by contextvars.ContextVar, so it is safe under both threads and async) automatically registers @js functions and use_state() initializers during route execution. You don't manage <script> tags manually β€” the HTML renderer collects everything registered in the context and places the resulting <script> tags in <head> for you.

This works in both sync and async handlers β€” the render context is propagated to the executor thread that runs sync handlers.


Project Structure

htealeaf/
β”œβ”€ elements/
β”‚  β”œβ”€ elements # HTML elements like h1, button ...
β”‚  β”œβ”€ component # Core HTML component class
β”‚  └─ renderer/ # HTML rendering + render context
β”œβ”€ server/
β”‚  β”œβ”€ server # Server core logic
β”‚  β”œβ”€ session # Session management
β”‚  β”œβ”€ http/ # Request / Response / Header
β”‚  └─ adapter/
β”‚     β”œβ”€ wsgi # WSGI adapter
β”‚     β”œβ”€ asgi # ASGI adapter
β”‚     └─ cgi # CGI adapter
β”œβ”€ js/
β”‚  β”œβ”€ jscode # JSCode, JSFunction, @js decorator
β”‚  β”œβ”€ py2js # Python β†’ JS transpiler
β”‚  └─ common # Ready-made JSCode helpers (document, console, ...)
└─ state/
   β”œβ”€ store # Store, AuthStore, SuperStore
   └─ local_state # use_state() / LocalState

Installation

⚠️ This wiki documents the develop branch. The current PyPI release (0.3.4) still ships the older CamelCase package (HTeaLeaf.Elements, HTeaLeaf.Server, …) and the examples in this wiki will not run against it. Until the next release, install from the development branch:

pip install "git+https://github.com/Az107/HTeaLeaf@develop"

Quick Example

from htealeaf import HteaLeaf, adapters, use_state
from htealeaf.js import js
from htealeaf.elements import div, button, h1

app = HteaLeaf(adapters.ASGI)
 
@app.route("/")
async def index():
    count = use_state(0)
    
    @js
    def increment():
        count.set(count.get() + 1)
        
    return div(
        h1("Count: ", count),
        button("Click me").attr(onclick=increment()),
    )

Wiki Pages

Suggested reading order:

  1. Getting Started β€” install, minimal app, run it
  2. Routing β€” defining routes and handlers
  3. Declarative HTML Elements β€” building HTML from Python components
  4. State management
  5. JSCode and JS from Python β€” writing client-side logic in Python

Related Projects

Project Language Role
HTeaPot Rust HTTP server
HTeaLeaf Python SSR framework (this project)
Cafetera Rust API mocker

Clone this wiki locally