- Rust transpiler (
.hyper->.py) - Component model with slots
- All Python control flow
- Streaming generator output
- Attribute system (boolean, class, style, data, aria, spread)
- Compile-time HTML validation
- Content collections (JSON, YAML, TOML, Markdown)
- JetBrains IDE plugin
- TextMate syntax bundle
- Contact the owner of the
hyperPyPI project about a transfer - PyPI package release
- JetBrains Marketplace release
- Fix blank line handling —
newline_is_content()can't tell a line ending from a blank line. The generator works around this withsaturating_sub(1), which fixes component bodies but eats real blank lines (seenested.hyper). - Document whitespace semantics in
docs/design/templates.mdanddocs/implementation/templates.md - Support multiline HTML tags (
<div\n class="card">) - Interactive inspector: bidirectional source <-> compiled highlighting
- Collapse inspector to single tab with Python/HTML/Boilerplate toggles
- Blank lines before combinable content are swallowed — a blank line between a comment/statement and an element disappears because
emit_combined_nodestrims leading\n. Blank lines before statements work fine (not combinable). This is why the compiled output is missing blank lines between section headers and HTML content. -
yield frominvalid in async templates — if a template usesasync for/async with, the whole function becomesasync def, making allyield from Component()calls invalid Python. The compiler needs to emit something different for async component calls. - Two blank lines between sections become one — source has 2 blank lines, output has 1. Related to the combining/trimming issue above.
- Newline between attributes breaks the tag — when an opening tag splits attributes across lines (
<div id="x"\n class="y">), the tokenizer closes the tag after the first attribute and emits the rest as text content. Only a single attribute's value may currently wrap (newlines inside the quotes are fine); workaround is to keep all attributes on the tag's first line with only the last value wrapping. - Parser panics on multibyte chars inside inline
{...}expressions — a non-ASCII char in an expression embedded in template text (e.g.{x or "aprofundată"}) panics attree_builder.rs:1172with "byte index N is not a char boundary", because the span offset is computed in bytes not char boundaries. Workaround is to compute the string in the Python zone and emit an ASCII-named variable instead. - No way to emit a literal
{/}— any brace in template text or an attribute is read as an expression delimiter, which breaks inline JS/JSON (e.g.onclick="if(c){f()}"ordata-config='{"k":"v"}').{{/}}only stays literal in lines with no real expression (where it renders doubled, not collapsed), and\{produces broken Python; there is no escape that yields a single literal brace, so the only workaround is to avoid braces entirely or move the value into a{expression}that returns the string. - Reserved Python keywords as component attributes emit invalid kwargs — a component call with an attribute that is a Python keyword (
<{Dropdown} class="...">) compiles toDropdown(..., class="..."), which is a syntax error becauseclassis reserved (alsofor,import, etc.). The compiler should emit reserved-word attributes on component calls as**{"class": "..."}instead of a bare kwarg. Workaround is to write the spread by hand in the source:<{Dropdown} {**{"class": "..."}}>. - Spread
{**kwargs}does not mergeclasswith a literalclass— when a tag has both a literalclass="base"and a{**kwargs}spread whose dict containsclass, the output emits two separateclassattributes (class="base" class="caller") instead of merging them, so the browser silently drops the second. This makes the common "component with base classes + caller-supplied extra classes" pattern impossible via spread; the spread should detectclassand concatenate it onto the literal one.
- File-based routing
- SSR framework integrations (FastAPI, Django, Flask)
- Static site generation
- Component partial rendering for htmx
- VS Code extension