How to get HTMX Target from Web Requests in F# Giraffe

Date: 2024-08-02 | create | tech | fsharp | giraffe | htmx |

I build most of my projects with F# / Giraffe and HTMX. In this post I'll share how I get HTMX Targets from a web request in F# / Giraffe as well as how I use that to build apps with HTMX.

Want to see HTMX in action in real projects? HAMINIONs members get access to the full source code of dozens of my projects including 3 using F# + Giraffe + HTMX.

How HTMX Targets Work

An HTMX Target (declared with hx-target) allows you to target a specific element on the page to swap returned HTML with. I like to build my apps with isolated Interactive Islands powered by HTMX so I use targets as a unique identifier for a specific element.

<div>
    <div id="iamanelement">
        <button hx-post="/do-something" hx-target="#iamanelement">
            Do something!
        </button>
    </div>
</div>

The way this works then is:

  • HTMX request is triggered including a Target it will put returned HTML into
  • Endpoint reads the Target from the web request
  • Endpoint returns HTML based on the request / Target
  • Browser swaps the HTML into the target

Get HTMX Target from a Web Request in F# / Giraffe

Now let's look at how to get an HTMX Target from an F# / Giraffe web request. Giraffe uses an HttpContext to access data on the request.

HTMX itself sends two Headers on requests that we care about:

  • HX-Request - Whether this is an HTMX request or not
  • HX-Target - The Target on the element

So we can create a little function that will get the target off of an HttpContext if it exists:

  • Checks if it's an HTMX Request
  • If so tries to get the target
let getHtmxTargetFromContext
    (ctx : HttpContext)
    : 'a option
    =
    ctx.TryGetRequestHeader "HX-Request"
    |> Option.bind (
        fun _ -> 
            ctx.TryGetRequestHeader "HX-Target"
    )

How I use HTMX Targets in Production WebApps

I usually setup my Server-side rendering code like this:

  • Endpoint - Parses request data into page props
  • Page - Renders full page (like an MPA) by composing smaller components. Conditionals on Target to only render specific components of the page.
  • Components - Smaller pure-ish components that are easy to compose.

This makes it easy for me to reuse components, get the power of HTMX for partial re-rendering where necessary, all while keeping most of the simplicity of MPAs.

Then you can do something like this to pass the target into your rendering code.

let myPageHttpHandler 
    = 
    handleContext(fun (ctx : HttpContext) -> 
        task {
            let target = 
                getHtmxTargetFromContext
                    ctx

            return renderMyComponent target
        }
    )

Your rendering code could be smth very simple like:

  • If no target -> render full thing
  • If target -> just render a part of that

This is exactly what I did in my latest project 1000 Checkboxes - if the target is the CheckboxGrid we only return that.

HAMINIONs Members get full access to project source code like 1000 checkboxes (Project Repo).

Next

I've been happily building little websites with F# + Giraffe + HTMX for the past several months. More to come.

Q: How are you building webapps with HTMX? What are you finding difficult to implement?

If you liked this post you might also like:

Want more like this?

The best / easiest way to support my work is by subscribing for future updates and sharing with your network.