Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/volfpeter/fasthx
FastAPI server-side rendering with built-in HTMX support.
https://github.com/volfpeter/fasthx
fastapi html jinja2 server-side-rendering templating website
Last synced: 3 days ago
JSON representation
FastAPI server-side rendering with built-in HTMX support.
- Host: GitHub
- URL: https://github.com/volfpeter/fasthx
- Owner: volfpeter
- License: mit
- Created: 2024-01-24T09:31:18.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-01-03T11:04:29.000Z (24 days ago)
- Last Synced: 2025-01-17T13:08:27.899Z (10 days ago)
- Topics: fastapi, html, jinja2, server-side-rendering, templating, website
- Language: Python
- Homepage: https://volfpeter.github.io/fasthx/
- Size: 866 KB
- Stars: 480
- Watchers: 8
- Forks: 10
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Funding: .github/funding.yml
- License: LICENSE
Awesome Lists containing this project
- awesome-python-htmx - FastHX - FastAPI-a9bbcc?style=flat&logo=fastapi&logoColor=black" alt="FastAPI"></a><br> (Third Party Packages π¦ <a name = "tools"></a> / Helper Libraries)
README
![Tests](https://github.com/volfpeter/fasthx/actions/workflows/tests.yml/badge.svg)
![Linters](https://github.com/volfpeter/fasthx/actions/workflows/linters.yml/badge.svg)
![Documentation](https://github.com/volfpeter/fasthx/actions/workflows/build-docs.yml/badge.svg)
![PyPI package](https://img.shields.io/pypi/v/fasthx?color=%2334D058&label=PyPI%20Package)**Source code**: [https://github.com/volfpeter/fasthx](https://github.com/volfpeter/fasthx)
**Documentation and examples**: [https://volfpeter.github.io/fasthx](https://volfpeter.github.io/fasthx/)
# FastHX
FastAPI server-side rendering with built-in HTMX support.
Key features:
- **Decorator syntax** that works with FastAPI as one would expect, no need for unused or magic dependencies in routes.
- Built for **HTMX**, but can be used without it.
- Works with **any templating engine** or server-side rendering library, e.g. `htmy`, `jinja2`, or `dominate`.
- Gives the rendering engine **access to all dependencies** of the decorated route.
- HTMX **routes work as expected** if they receive non-HTMX requests, so the same route can serve data and render HTML at the same time.
- **Response headers** you set in your routes are kept after rendering, as you would expect in FastAPI.
- **Correct typing** makes it possible to apply other (typed) decorators to your routes.
- Works with both **sync** and **async routes**.## Installation
The package is available on PyPI and can be installed with:
```console
$ pip install fasthx
```The package has optional dependencies for the following **official integrations**:
- [htmy](https://volfpeter.github.io/htmy/): `pip install fasthx[htmy]`.
- [jinja](https://jinja.palletsprojects.com/en/stable/): `pip install fasthx[jinja]`.## Examples
For complete, but simple examples that showcase the basic use of `FastHX`, please see the [examples](https://github.com/volfpeter/fasthx/tree/main/examples) folder.
### HTMY templating
Requires: `pip install fasthx[htmy]`.
Serving HTML and HTMX requests with [htmy](https://volfpeter.github.io/htmy/) is as easy as creating a `fasthx.htmy.HTMY` instance and using its `hx()` and `page()` decorator methods on your routes.
The example below assumes the existence of an `IndexPage` and a `UserList` `htmy` component. The full working example with the `htmy` components can be found [here](https://github.com/volfpeter/fasthx/tree/main/examples/htmy-rendering).
```python
from datetime import datefrom fastapi import FastAPI
from pydantic import BaseModelfrom fasthx.htmy import HTMY
# Pydantic model for the application
class User(BaseModel):
name: str
birthday: date# Create the FastAPI application.
app = FastAPI()# Create the FastHX HTMY instance that renders all route results.
htmy = HTMY()@app.get("/users")
@htmy.hx(UserList) # Render the result using the UserList component.
def get_users(rerenders: int = 0) -> list[User]:
return [
User(name="John", birthday=date(1940, 10, 9)),
User(name="Paul", birthday=date(1942, 6, 18)),
User(name="George", birthday=date(1943, 2, 25)),
User(name="Ringo", birthday=date(1940, 7, 7)),
]@app.get("/")
@htmy.page(IndexPage) # Render the index page.
def index() -> None: ...
```### Jinja2 templating
Requires: `pip install fasthx[jinja]`.
To start serving HTML and HTMX requests, all you need to do is create an instance of `fasthx.Jinja` and use its `hx()` or `page()` methods as decorators on your routes. `hx()` only triggers HTML rendering for HTMX requests, while `page()` unconditionally renders HTML. See the example code below:
```python
from fastapi import FastAPI
from fastapi.templating import Jinja2Templates
from fasthx import Jinja
from pydantic import BaseModel# Pydantic model of the data the example API is using.
class User(BaseModel):
first_name: str
last_name: str# Create the app.
app = FastAPI()# Create a FastAPI Jinja2Templates instance and use it to create a
# FastHX Jinja instance that will serve as your decorator.
jinja = Jinja(Jinja2Templates("templates"))@app.get("/")
@jinja.page("index.html")
def index() -> None:
...@app.get("/user-list")
@jinja.hx("user-list.html")
async def htmx_or_data() -> list[User]:
return [
User(first_name="John", last_name="Lennon"),
User(first_name="Paul", last_name="McCartney"),
User(first_name="George", last_name="Harrison"),
User(first_name="Ringo", last_name="Starr"),
]@app.get("/admin-list")
@jinja.hx("user-list.html", no_data=True)
def htmx_only() -> list[User]:
return [User(first_name="Billy", last_name="Shears")]
```See the full working example [here](https://github.com/volfpeter/fasthx/tree/main/examples/jinja-rendering).
### Custom templating
Requires: `pip install fasthx`.
If you would like to use a rendering engine without FastHX integration, you can easily build on the `hx()` and `page()` decorators which give you all the functionality you will need. All you need to do is implement the `HTMLRenderer` protocol.
Similarly to the Jinja case, `hx()` only triggers HTML rendering for HTMX requests, while `page()` unconditionally renders HTML. See the example code below:
```python
from typing import Annotated, Anyfrom fastapi import Depends, FastAPI, Request
from fasthx import hx, page# Create the app.
app = FastAPI()# Create a dependecy to see that its return value is available in the render function.
def get_random_number() -> int:
return 4 # Chosen by fair dice roll.DependsRandomNumber = Annotated[int, Depends(get_random_number)]
# Create the render methods: they must always have these three arguments.
# If you're using static type checkers, the type hint of `result` must match
# the return type annotation of the route on which this render method is used.
def render_index(result: list[dict[str, str]], *, context: dict[str, Any], request: Request) -> str:
return "Hello FastHX
"def render_user_list(result: list[dict[str, str]], *, context: dict[str, Any], request: Request) -> str:
# The value of the `DependsRandomNumber` dependency is accessible with the same name as in the route.
random_number = context["random_number"]
lucky_number = f"{random_number}
"
users = "".join(("
- ", *(f"
- {u['name']} " for u in result), "
return f"{lucky_number}\n{users}"
@app.get("/")
@page(render_index)
def index() -> None:
...
@app.get("/htmx-or-data")
@hx(render_user_list)
def htmx_or_data(random_number: DependsRandomNumber) -> list[dict[str, str]]:
return [{"name": "Joe"}]
@app.get("/htmx-only")
@hx(render_user_list, no_data=True)
async def htmx_only(random_number: DependsRandomNumber) -> list[dict[str, str]]:
return [{"name": "Joe"}]
```
See the full working example [here](https://github.com/volfpeter/fasthx/tree/main/examples/custom-rendering).
### External examples
- [FastAPI-HTMX-Tailwind example](https://github.com/volfpeter/fastapi-htmx-tailwind-example): A complex `Jinja2` example with features like active search, lazy-loading, server-sent events, custom server-side HTMX triggers, dialogs, and TailwindCSS and DaisyUI integration.
## Dependencies
The only dependency of this package is `fastapi`.
## Development
Use `ruff` for linting and formatting, `mypy` for static code analysis, and `pytest` for testing.
The documentation is built with `mkdocs-material` and `mkdocstrings`.
## Contributing
Feel free to ask questions or request new features.
And of course all contributions are welcome, including more documentation, examples, code, and tests.
The goal is to make `fasthx` a well-rounded project that makes even your most complex HTMX use-cases easy to implement.
## Contributors
- [Peter Volf](https://github.com/volfpeter)
- [Hasan Sezer TaΕan](https://github.com/hasansezertasan)
## License - MIT
The package is open-sourced under the conditions of the [MIT license](https://choosealicense.com/licenses/mit/).
## Thank you
Thank you to [Smart-Now](https://www.smart-now.com/) for supporting the project.