Skip to content

Async rendering

htpy fully supports rendering HTML asynchronously. Combined with a async framework such as Starlette/FastAPI, the entire web request can be processed async and the HTML page can be sent to the client incrementally as soon as it is ready.

Async components

In addition to regular, synchronous components, components can be defined as an async def coroutine. When rendering, htpy will await all async components:

from htpy import li
import asyncio

async def get_text() -> str:
    return "hi!"

async def my_text() -> Renderable:
    results = await get_text()
    return p[results]

Async iterators

htpy will consume async iterators:

from htpy import ul, li

async def my_items() -> AsyncIterator[Renderable]:
    yield li["a"]
    yield li["b"]

def my_list() -> Renderable:
    return ul[my_items()]

Rendering async content

To retrieve results from async rendering, use the aiter_chunks() method. It returns an async iterator that yields the HTML document as bytes.

import asyncio

from htpy import p

my_paragraph = p["hello!"]


async def main() -> None:
    async for chunk in my_paragraph.aiter_chunks():
        print(chunk)


asyncio.run(main())

# output:
# <p>
# hello!
# </p>

The async iterator returned by aiter_chunks() can be passed to your web framework's streaming response class. See the htpy Starlette docs for more information how to integrate with Starlette.

Warning

Trying to get the string value of an async renderable like str(element) will result an exception:

Traceback (most recent call last):

  File "/Users/andreas/code/htpy/examples/async_in_sync_context.py", line 7, in <module>
    str(div[my_async_component()])
    ~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^

TypeError: <coroutine object my_async_component at 0x103471010> is not a valid child element.
           Use the `.aiter_chunks()` method to retrieve the content: https://htpy.dev/async/

Instead, use aiter_chunks():

async for chunk in div[my_async_component()].aiter_chunks():
    print(chunk)