A Hyper component returns safe HTML when called and yields chunks through .stream():
html = Greeting(name="Ada")
chunks = Greeting.stream(name="Ada")Framework integrations only need to set the HTML response type.
-
Add the extension to your
Environment:from jinja2 import Environment, FileSystemLoader env = Environment(loader=FileSystemLoader("templates")) env.add_extension("hyperhtml.integrations.jinja2.HyperExtension") # env.register_components(myapp.other.path.to.components) # register components outside templates/
-
Add
Greeting.hyperto thetemplates/folder. -
Call it by name in any template:
{{ Greeting(name="Ada") }} {# → <h1>Hello Ada</h1> #}
Card.hyper has a default slot ({...}) and a named <{...actions}> slot:
title: str
---
<section class="card">
<h2>{title}</h2>
# Default slot
{...}
<footer>
# Named slot
<{...actions}>
<span>No actions</span>
</{...actions}>
</footer>
</section>
Fill them by wrapping the call in {% hyper %}:
{% hyper Card(title="Pricing") %}
<p>Three tiers, no surprises.</p>
{% slot actions %}<a href="/buy">Buy now</a>{% endslot %}
{% endhyper %}<section class="card">
<h2>Pricing</h2>
<p>Three tiers, no surprises.</p>
<footer><a href="/buy">Buy now</a></footer>
</section>Spread a dict with **, like Python:
{{ Card(**props) }}
{% hyper Card(title="Pricing", **props) %}…{% endhyper %}-
Add the app to
INSTALLED_APPS:INSTALLED_APPS = [ ..., "hyperhtml.integrations.django", ]
-
Register the tag as a builtin, so you skip
{% load hyper %}:TEMPLATES = [{ "BACKEND": "django.template.backends.django.DjangoTemplates", "OPTIONS": { "context_processors": [ "hyperhtml.integrations.django.context_processors.components", ], "builtins": [ "hyperhtml.integrations.django.templatetags.hyper", ], }, }]
-
Add
Greeting.hyperto anytemplates/folder. Hyper finds it wherever Django looks for templates (each app'stemplates/, plus the backend'sDIRS). -
Call a component:
{% hyper Greeting name=user.first_name / %}
→
<h1>Hello Ada</h1>
A trailing / self-closes a no-slot tag (note the space before %}).
To fill slots, drop the / and close with {% endhyper %}:
{% hyper Card title="Pricing" %}
<p>Three tiers, no surprises.</p>
{% slot actions %}<a href="/buy">Buy now</a>{% endslot %}
{% endhyper %}<section class="card">
<h2>Pricing</h2>
<p>Three tiers, no surprises.</p>
<footer><a href="/buy">Buy now</a></footer>
</section>Spread a dict with **, like Python:
{% hyper Card title="Pricing" **props %}…{% endhyper %}-
Add the extension to the Jinja2 backend's
OPTIONS:TEMPLATES = [{ "BACKEND": "django.template.backends.jinja2.Jinja2", "DIRS": [BASE_DIR / "templates"], "APP_DIRS": True, "OPTIONS": { "extensions": ["hyperhtml.integrations.jinja2.HyperExtension"], }, }]
-
Call components with the Jinja syntax from above.
Both backends run side by side without clashing.
-
Return a component. Set
response_classtoHTMLResponse:from fastapi.responses import HTMLResponse @app.get("/", response_class=HTMLResponse) def index(): return Greeting(name="Ada")
-
Stream with
StreamingResponse:from fastapi.responses import StreamingResponse @app.get("/stream") def stream(): return StreamingResponse(Greeting.stream(name="Ada"), media_type="text/html")
-
Return a component. Set
media_typetoMediaType.HTML:from litestar import get, MediaType @get("/", media_type=MediaType.HTML) async def index() -> str: return Greeting(name="Ada")
-
Stream with
Stream:from litestar import get, MediaType from litestar.response import Stream @get("/stream", media_type=MediaType.HTML) async def stream() -> Stream: return Stream(Greeting.stream(name="Ada"))
-
Return a component with
response.html:from sanic import response @app.get("/") async def index(request): return response.html(Greeting(name="Ada"))
-
Stream with
ResponseStream:from sanic.response import ResponseStream @app.get("/stream") async def stream(request): async def body(res): for chunk in Greeting.stream(name="Ada"): await res.write(chunk) return ResponseStream(body, content_type="text/html")
-
Return a component. A returned string is already HTML:
@app.get("/") def index(): return Greeting(name="Ada")
-
Stream any iterator:
@app.get("/stream") def stream(): return Greeting.stream(name="Ada")