Skip to content

[FEATURE] It is hoped that the hx_post parameter can support directly assigning functions decorated by router to implement reverse routing. #864

@kaluluosi

Description

@kaluluosi

If you'd like to discuss your feature idea first with the community (highly recommended!) please visit our Discord channel.

Is your feature request related to a problem? Please describe.
I believe hardcoding route strings directly in FastHTML's hx_* attributes is poor practice, especially as projects grow larger and undergo early‑stage iteration. This creates significant friction when distinguishing view vs. action routes, refactoring paths, or maintaining consistent routing across the codebase.

Most full‑stack frameworks provide a reverse‑routing mechanism to solve this problem, but I have not found an equivalent function in FastHTML.

Describe the solution you'd like
I initially planned to request a url_for(name: str) utility function that returns the URL for a named route.

However, I was pleasantly surprised to discover that hx_* attributes already support passing route functions directly, as they are implicitly converted to strings. The following example works perfectly:

from fasthtml.common import (
    fast_app,
    Div,
    P,
    serve,
    Button,
    Main,
    H1,
    A,
    Form,
    Input,
    Title,
)

app, router = fast_app()

count = 0

@router("/")
def home():
    return Title("Count Demo"), Main(
        H1("Count Demo"),
        P(f"Count is set to {count}", id="count"),
        Button(
            "Increment", hx_post=str(increment), hx_target="#count", hx_swap="innerHTML"
        ),
    )


@router("/increment")
def increment():
    print(increment, type(increment))
    print("incrementing")
    global count
    count += 1
    return f"Count is set to {count}"


serve()

In other words, FastHTML already supports reverse routing: simply convert a route function to a string with str(), and the result is its corresponding path, which can then be passed to hx_post and similar attributes.

Therefore, my proposal has evolved. I would like the maintainers to evaluate the validity of this pattern and consider:

  1. Automatically converting values passed to hx_post, hx_get, hx_put, etc., to strings internally, so users no longer need to explicitly call str().
  2. Updating the type annotations for hx_post, hx_get, hx_put, and related attributes to accept route functions directly.

Example code

from fasthtml.common import (
    fast_app,
    Div,
    P,
    serve,
    Button,
    Main,
    H1,
    A,
    Form,
    Input,
    Title,
)

app, router = fast_app()

count = 0

@router("/")
def home():
    return Title("Count Demo"), Main(
        H1("Count Demo"),
        P(f"Count is set to {count}", id="count"),
        # NOTE: hx_post directly accepts the increment function and converts it to str internally
        Button(
            "Increment", hx_post=increment, hx_target="#count", hx_swap="innerHTML"
        ),
    )


@router("/increment")
def increment():
    print(increment, type(increment))
    print("incrementing")
    global count
    count += 1
    return f"Count is set to {count}"


serve()
# Your example code here

Similar implementations
If available, provide links to similar features in other libraries:

  1. Library Name
  2. Another Library

Problem solved
This improvement eliminates the need to hardcode route strings throughout the codebase, enabling type‑safe, refactor‑friendly reverse routing. Users will benefit from fewer routing errors, easier path renaming, cleaner code, and better IDE support when referencing routes. It aligns FastHTML’s syntax with modern full‑stack framework conventions while leveraging existing internal behavior.

Additional context
This pattern already works today with explicit str() conversion, suggesting the underlying implementation is stable. Formalizing it would improve developer experience without breaking changes.

Confirmation
Please confirm the following:

  • I have checked the existing issues and pull requests to ensure this feature hasn't been requested before.
  • I have read the project's documentation to ensure this feature doesn't already exist.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions