{"id":13780542,"url":"https://github.com/volfpeter/fasthx","last_synced_at":"2025-05-15T00:13:07.088Z","repository":{"id":218870081,"uuid":"747609684","full_name":"volfpeter/fasthx","owner":"volfpeter","description":"FastAPI server-side rendering with built-in HTMX support.","archived":false,"fork":false,"pushed_at":"2025-03-14T13:02:32.000Z","size":828,"stargazers_count":519,"open_issues_count":4,"forks_count":13,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-03-30T19:01:43.624Z","etag":null,"topics":["fastapi","html","jinja2","server-side-rendering","templating","website"],"latest_commit_sha":null,"homepage":"https://volfpeter.github.io/fasthx/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/volfpeter.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/funding.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"buy_me_a_coffee":"volfpeter"}},"created_at":"2024-01-24T09:31:18.000Z","updated_at":"2025-03-25T20:52:47.000Z","dependencies_parsed_at":"2024-02-19T17:53:46.908Z","dependency_job_id":"ca6ffe27-ba43-498a-9f2f-6cb6da0cd5dd","html_url":"https://github.com/volfpeter/fasthx","commit_stats":{"total_commits":99,"total_committers":2,"mean_commits":49.5,"dds":0.08080808080808077,"last_synced_commit":"e850b9c61fa2c8868a0f7a4b2b567934c773eee8"},"previous_names":["volfpeter/fasthx"],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/volfpeter%2Ffasthx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/volfpeter%2Ffasthx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/volfpeter%2Ffasthx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/volfpeter%2Ffasthx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/volfpeter","download_url":"https://codeload.github.com/volfpeter/fasthx/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247543583,"owners_count":20955865,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fastapi","html","jinja2","server-side-rendering","templating","website"],"created_at":"2024-08-03T18:01:16.994Z","updated_at":"2025-04-06T20:02:57.729Z","avatar_url":"https://github.com/volfpeter.png","language":"Python","readme":"![Tests](https://github.com/volfpeter/fasthx/actions/workflows/tests.yml/badge.svg)\n![Linters](https://github.com/volfpeter/fasthx/actions/workflows/linters.yml/badge.svg)\n![Documentation](https://github.com/volfpeter/fasthx/actions/workflows/build-docs.yml/badge.svg)\n![PyPI package](https://img.shields.io/pypi/v/fasthx?color=%2334D058\u0026label=PyPI%20Package)\n\n**Source code**: [https://github.com/volfpeter/fasthx](https://github.com/volfpeter/fasthx)\n\n**Documentation and examples**: [https://volfpeter.github.io/fasthx](https://volfpeter.github.io/fasthx/)\n\n# FastHX\n\nFastAPI server-side rendering with built-in HTMX support.\n\n## Key features\n\n- **Decorator syntax** that works with FastAPI as one would expect, no need for unused or magic dependencies in routes.\n- Built for **HTMX**, but can be used without it.\n- Works with **any templating engine** or server-side rendering library, e.g. `htmy`, `jinja2`, or `dominate`.\n- Gives the rendering engine **access to all dependencies** of the decorated route.\n- 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.\n- **Response headers** you set in your routes are kept after rendering, as you would expect in FastAPI.\n- **Correct typing** makes it possible to apply other (typed) decorators to your routes.\n- Works with both **sync** and **async routes**.\n\n## Support\n\nConsider supporting the development and maintenance of the project through [sponsoring](https://buymeacoffee.com/volfpeter), or reach out for [consulting](https://www.volfp.com/contact?subject=Consulting%20-%20FastHX) so you can get the most out of the library.\n\n## Installation\n\nThe package is available on PyPI and can be installed with:\n\n```console\n$ pip install fasthx\n```\n\nThe package has optional dependencies for the following **official integrations**:\n\n- [htmy](https://volfpeter.github.io/htmy/): `pip install fasthx[htmy]`.\n- [jinja](https://jinja.palletsprojects.com/en/stable/): `pip install fasthx[jinja]`.\n\n## Core concepts\n\nThe core concept of FastHX is to let FastAPI routes do their usual job of handling the business logic and returning the result, while the FastHX decorators take care\nof the entire rendering / presentation layer using a declarative, decorator-based approach.\n\nInterally, FastHX decorators always have access to the decorated route's result, all of its arguments (sometimes called the request context), and the current request. Integrations convert these values into data that can be consumed by the used rendering engine (for example `htmy` or `jinja`), run the rendering engine with the selected component (more on this below) and the created data, and return the result to the client. For more details on how data conversion works and how it can be customized, please see the API documentation of the rendering engine integration of your choice.\n\nThe `ComponentSelector` abstraction makes it possible to declaratively specify and dynamically select the component that should be used to render the response to a given request. It is also possible to define an \"error\" `ComponentSelector` that is used if the decorated route raises an exception -- a typical use-case being error rendering for incorrect user input.\n\n## Examples\n\nFor complete, but simple examples that showcase the basic use of `FastHX`, please see the [examples](https://github.com/volfpeter/fasthx/tree/main/examples) folder.\n\n### HTMY templating\n\nRequires: `pip install fasthx[htmy]`.\n\nServing 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.\n\nThe 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).\n\n```python\nfrom datetime import date\n\nfrom fastapi import FastAPI\nfrom pydantic import BaseModel\n\nfrom fasthx.htmy import HTMY\n\n# Pydantic model for the application\nclass User(BaseModel):\n    name: str\n    birthday: date\n\n# Create the FastAPI application.\napp = FastAPI()\n\n# Create the FastHX HTMY instance that renders all route results.\nhtmy = HTMY()\n\n@app.get(\"/users\")\n@htmy.hx(UserList)  # Render the result using the UserList component.\ndef get_users(rerenders: int = 0) -\u003e list[User]:\n    return [\n        User(name=\"John\", birthday=date(1940, 10, 9)),\n        User(name=\"Paul\", birthday=date(1942, 6, 18)),\n        User(name=\"George\", birthday=date(1943, 2, 25)),\n        User(name=\"Ringo\", birthday=date(1940, 7, 7)),\n    ]\n\n@app.get(\"/\")\n@htmy.page(IndexPage)  # Render the index page.\ndef index() -\u003e None: ...\n```\n\n### Jinja2 templating\n\nRequires: `pip install fasthx[jinja]`.\n\nTo 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:\n\n```python\nfrom fastapi import FastAPI\nfrom fastapi.templating import Jinja2Templates\nfrom fasthx import Jinja\nfrom pydantic import BaseModel\n\n# Pydantic model of the data the example API is using.\nclass User(BaseModel):\n    first_name: str\n    last_name: str\n\n# Create the app.\napp = FastAPI()\n\n# Create a FastAPI Jinja2Templates instance and use it to create a\n# FastHX Jinja instance that will serve as your decorator.\njinja = Jinja(Jinja2Templates(\"templates\"))\n\n@app.get(\"/\")\n@jinja.page(\"index.html\")\ndef index() -\u003e None:\n    ...\n\n@app.get(\"/user-list\")\n@jinja.hx(\"user-list.html\")\nasync def htmx_or_data() -\u003e list[User]:\n    return [\n        User(first_name=\"John\", last_name=\"Lennon\"),\n        User(first_name=\"Paul\", last_name=\"McCartney\"),\n        User(first_name=\"George\", last_name=\"Harrison\"),\n        User(first_name=\"Ringo\", last_name=\"Starr\"),\n    ]\n\n@app.get(\"/admin-list\")\n@jinja.hx(\"user-list.html\", no_data=True)\ndef htmx_only() -\u003e list[User]:\n    return [User(first_name=\"Billy\", last_name=\"Shears\")]\n```\n\nSee the full working example [here](https://github.com/volfpeter/fasthx/tree/main/examples/jinja-rendering).\n\n### Custom templating\n\nRequires: `pip install fasthx`.\n\nIf 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.\n\nSimilarly to the Jinja case, `hx()` only triggers HTML rendering for HTMX requests, while `page()` unconditionally renders HTML. See the example code below:\n\n```python\nfrom typing import Annotated, Any\n\nfrom fastapi import Depends, FastAPI, Request\nfrom fasthx import hx, page\n\n# Create the app.\napp = FastAPI()\n\n# Create a dependecy to see that its return value is available in the render function.\ndef get_random_number() -\u003e int:\n    return 4  # Chosen by fair dice roll.\n\nDependsRandomNumber = Annotated[int, Depends(get_random_number)]\n\n# Create the render methods: they must always have these three arguments.\n# If you're using static type checkers, the type hint of `result` must match\n# the return type annotation of the route on which this render method is used.\ndef render_index(result: list[dict[str, str]], *, context: dict[str, Any], request: Request) -\u003e str:\n    return \"\u003ch1\u003eHello FastHX\u003c/h1\u003e\"\n\ndef render_user_list(result: list[dict[str, str]], *, context: dict[str, Any], request: Request) -\u003e str:\n    # The value of the `DependsRandomNumber` dependency is accessible with the same name as in the route.\n    random_number = context[\"random_number\"]\n    lucky_number = f\"\u003ch1\u003e{random_number}\u003c/h1\u003e\"\n    users = \"\".join((\"\u003cul\u003e\", *(f\"\u003cli\u003e{u['name']}\u003c/li\u003e\" for u in result), \"\u003c/ul\u003e\"))\n    return f\"{lucky_number}\\n{users}\"\n\n@app.get(\"/\")\n@page(render_index)\ndef index() -\u003e None:\n    ...\n\n@app.get(\"/htmx-or-data\")\n@hx(render_user_list)\ndef htmx_or_data(random_number: DependsRandomNumber) -\u003e list[dict[str, str]]:\n    return [{\"name\": \"Joe\"}]\n\n@app.get(\"/htmx-only\")\n@hx(render_user_list, no_data=True)\nasync def htmx_only(random_number: DependsRandomNumber) -\u003e list[dict[str, str]]:\n    return [{\"name\": \"Joe\"}]\n```\n\nSee the full working example [here](https://github.com/volfpeter/fasthx/tree/main/examples/custom-rendering).\n\n### External examples\n\n- [lipsum-chat](https://github.com/volfpeter/lipsum-chat): A simple chat application using `htmy` for server-side rendering, and HTMX, TailwindCSS v4 and DaisyUI v5 for the frontend.\n- [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.\n\n## Dependencies\n\nThe only dependency of this package is `fastapi`.\n\n## Development\n\nUse `ruff` for linting and formatting, `mypy` for static code analysis, and `pytest` for testing.\n\nThe documentation is built with `mkdocs-material` and `mkdocstrings`.\n\n## Contributing\n\nWe welcome contributions from the community to help improve the project! Whether you're an experienced developer or just starting out, there are many ways you can contribute:\n\n- **Discuss**: Join our [Discussion Board](https://github.com/volfpeter/fasthx/discussions) to ask questions, share ideas, provide feedback, and engage with the community.\n- **Document**: Help improve the documentation by fixing typos, adding examples, and updating guides to make it easier for others to use the project.\n- **Develop**: Prototype requested features or pick up issues from the issue tracker.\n- **Share**: Share your own project by adding it to the external examples list, helping others discover and benefit from your work.\n- **Test**: Write tests to improve coverage and enhance reliability.\n\n## License - MIT\n\nThe package is open-sourced under the conditions of the [MIT license](https://choosealicense.com/licenses/mit/).\n\n## Thank you\n\nThank you to [Smart-Now](https://www.smart-now.com/) for supporting the project.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"static/smart-now-logo.png\" height=\"42\" /\u003e\n\u003c/p\u003e\n","funding_links":["https://buymeacoffee.com/volfpeter"],"categories":["Third Party Packages 📦 \u003ca name = \"tools\"\u003e\u003c/a\u003e","Python"],"sub_categories":["Helper Libraries"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvolfpeter%2Ffasthx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvolfpeter%2Ffasthx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvolfpeter%2Ffasthx/lists"}