pythonhtmxfasthtml

How to update the content of a div with a button using FastHTML


Using FastHTML, I would like to create a navigation bar that updates a div's content based on the item clicked.

I wrote the following piece of code, but nothing happens when I click the buttons: the #page-content div doesn't update, and I don't see the logs of the GET methods being triggered.

from fasthtml.common import *

app = FastHTML()

@app.route("/", methods=["get"])
def index():
    return Html(
        Body(
            Header(navigation_bar(["page1", "page2"])),
            Div(page1(), id="page-content"),
        ),
    )

def navigation_bar(navigation_items: list[str]):
    return Nav(
        Ul(
            *[
                Li(Button(item, hx_get=f"/{item}", hx_target="#page-content"))
                for item in navigation_items
            ]
        ),
    )

@app.route("/page1", methods=["get"])
def page1():
    return Div("You are on page 1.")

@app.route("/page2", methods=["get"])
def page2():
    return Div("You are on page 2.")

serve()

Here is the generated HTML:

<html>
  <head></head>
  <body>
    <header>
      <nav>
        <ul>
          <li>
            <button hx-get="/page1" hx-target="#page-content">page-1</button>
          </li>
          <li>
            <button hx-get="/page2" hx-target="#page-content">page-2</button>
          </li>
        </ul>
      </nav>
    </header>
    <div id="page-content"><div>You are on page 1.</div></div>
  </body>
</html>

I think the HTML is generated correctly, as the tags' attributes related to HTMX look like what I could see on online examples. Where could the issue be?


Solution

  • The issue came from the fact that I was wrapping my Body component into an Html component manually. Removing the Html component, FastHTML takes care of the wrapping automatically and also includes the required scripts.

    Here is my updated piece of code:

    from fasthtml.common import *
    
    app = FastHTML()
    
    @app.route("/", methods=["get"])
    def index():
        return Body(
            Header(navigation_bar(["page1", "page2"])),
            Div(page1(), id="page-content"),
        )
    
    def navigation_bar(navigation_items: list[str]):
        return Nav(
            Ul(
                *[
                    Li(Button(item, hx_get=f"/{item}", hx_target="#page-content"))
                    for item in navigation_items
                ]
            ),
        )
    
    @app.route("/page1", methods=["get"])
    def page1():
        return Div("You are on page 1.")
    
    @app.route("/page2", methods=["get"])
    def page2():
        return Div("You are on page 2.")
    
    serve()