Skip to content

scriptogre/hyper

Repository files navigation

Hyper

CI License: MIT

Backend-neutral, type-safe HTML templates for Python, powered by Rust.

uv add hyperhtml

Write .hyper files and import them like Python components. No build step, no generated .py files.

Quick Start

1. Write a template. Props go above the ---, template body below.

# app/components/Greeting.hyper

name: str

---

<h1>Hello, {name}!</h1>

2. Use it.

from app.components import Greeting

@app.get("/", response_class=HTMLResponse)
def index():
    return Greeting(name="World")

Hyper compiles Greeting.hyper on import and caches it for the life of the process.

Features

Components

Components compose like HTML elements. Children go in the default slot with {...}.

# app/components/ProductCard.hyper

name: str
price: float
image: str
on_sale: bool = False

---

<div class={["card", {"sale": on_sale}]}>
    <img src={image} alt={name} />
    <h3>{name}</h3>
    if on_sale:
        <span class="badge">Sale</span>
    end
    <p class="price">${price:.2f}</p>
</div>
# app/pages/Store.hyper
from app.layouts import Layout
from app.components import ProductCard

products: list[Product]

---

<{Layout} title="Store">
    for product in products:
        <{ProductCard} name={product.name} price={product.price} image={product.image} on_sale={product.on_sale} />
    end
</{Layout}>

Control flow

The template body is the function body. Any valid Python works. Blocks end with end.

from app.enums import Status
from app.models import Product

status: Status
products: list[Product]

---

match status:
    case Status.LOADING:
        <div class="spinner" />
    case Status.ERROR:
        <p class="error">Something went wrong</p>
    case Status.OK:
        if not products:
            <p>No products found</p>
        else:
            <ul>
                for product in products:
                    <li>{product.name}</li>
                end
            </ul>
        end
end

Streaming

Every component is a generator, so streaming works out of the box:

from fastapi.responses import StreamingResponse
from app.pages import Store

@app.get("/store")
def store():
    return StreamingResponse(Store.stream(products=products), media_type="text/html")

IDE Support

  • JetBrains (PyCharm, IntelliJ) — Python intelligence inside .hyper files: autocomplete, go-to-definition, type checking, auto-transpilation for IDE analysis
  • TextMate / VS Code — Syntax highlighting

Acknowledgements

Hyper's component and attribute syntax was inspired by tdom.

Contributing

See CONTRIBUTING.md.

License

MIT

About

Type-safe templates for Python, powered by Rust.

Topics

Resources

License

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors