Skip to content

scriptogre/hyper

Repository files navigation

Hyper

CI PyPI License: MIT

Write type-safe HTML in .hyper files. Import components directly from Python.

uv add hyperhtml

Requires Python 3.10 or newer.

Create a component

Create app/pages/Greeting.hyper:

<h1>Hello, World!</h1>

Import and call it:

from app.pages import Greeting

print(Greeting())
<h1>Hello, World!</h1>

The filename sets the component name.

Pass data

Add typed props above ---:

name: str
---
<h1>Hello, {name}!</h1>
print(Greeting(name="Ada"))
<h1>Hello, Ada!</h1>

Props are keyword-only. Values are escaped by default.

Use Python

Use Python expressions and control flow directly:

names: list[str]
---
for name in names:
    <h1>Hello, {name}!</h1>
end
print(Greeting(names=["Ada", "Lin"]))
<h1>Hello, Ada!</h1><h1>Hello, Lin!</h1>

Close each indented block with end.

Compose components

Create app/components/Card.hyper:

title: str
---
<article><h2>{title}</h2></article>

Use it from app/pages/Dashboard.hyper:

from app.components import Card
---
<{Card} title="Orders" />
from app.pages import Dashboard

print(Dashboard())
<article><h2>Orders</h2></article>

Pass content

Place {...} where caller content belongs:

title: str
---
<article>
    <h2>{title}</h2>
    <div>{...}</div>
</article>

Pass content between component tags:

from app.components import Card
---
<{Card} title="Orders">
    <p>3 open orders</p>
</{Card}>
<article><h2>Orders</h2><div><p>3 open orders</p></div></article>

Pass named content

Use a named slot when content belongs in a specific place:

title: str
---
<article>
    <h2>{title}</h2>
    <div>{...}</div>
    <footer>{...actions}</footer>
</article>

Mark the element that fills it:

from app.components import Card
---
<{Card} title="Orders">
    <p>3 open orders</p>
    <button {...actions}>View orders</button>
</{Card}>
<article><h2>Orders</h2><div><p>3 open orders</p></div><footer><button>View orders</button></footer></article>

Define several components

Use component to group related components in one file:

# app/components/forms.hyper
component Button(*, label: str):
    <button>{label}</button>
end

component Input(*, name: str):
    <input name={name} />
end

A declarations-only file imports like a Python module:

from app.components.forms import Button, Input

print(Button(label="Save"))
<button>Save</button>

Use FastAPI

Set the response type to HTML, then return a component:

from fastapi import FastAPI
from fastapi.responses import HTMLResponse

from app.pages import Dashboard

app = FastAPI(default_response_class=HTMLResponse)


@app.get("/dashboard")
def dashboard():
    return Dashboard()

Stream a response

Use .stream() to send each generated chunk:

from fastapi.responses import StreamingResponse


@app.get("/dashboard/stream")
def stream_dashboard():
    return StreamingResponse(
        Dashboard.stream(),
        media_type="text/html",
    )

See Integrations for Django, Jinja, Flask, Litestar, and Sanic.

IDE support

  • JetBrains: syntax highlighting, Python language injection, and compiler diagnostics
  • TextMate and VS Code: syntax highlighting

Documentation

License

MIT

About

Type-safe templates for Python, powered by Rust.

Topics

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages