If you’ve done any frontend development in the past decade, you’ve likely interacted with Bootstrap, a toolkit focused on providing developers with CSS layout rules and components. Additionally, no frontend toolkit is complete with the accompanying JavaScript to make components interactive. That said, the JavaScript aspects are entirely optional, and the toolkit has a “Bring Your Own JavaScript” philosophy to integrate Bootstrap visual components into whatever frontend library you choose.

This post will show how to use Bootstrap modals with HTMX. For folks who aren’t aware, HTMX is a hypermedia-focused library to build interactivity into your client applications with server-rendered responses.

Please note I’m using the HTMX.NET library and ASP.NET Core tag helpers.

Bootstrap Modals

Bootstrap modals are built with HTML, CSS, and Javascript in mind. You typically have all three working together to get the intended behavior the Bootstrap team intended. You can find these interactive portions of bootstrap modals in the JavaScript library that accompanies the library, but there’s a caveat.

Modals have to already exist on the page as HTML or programmatically added through JavaScript calls. Static modals are great, but likely not what most folks want. Most developers want dynamic modals based on user interaction or situational modals. So how do we get dynamic modals that we generate on the server? Well, spoiler alert, HTMX, of course!

HTMX and All That Jazz

Before we start looking at the code, let’s look at the parts of the process we need to consider.

  1. The interactive element is a button a user will click to trigger a request to the server.
  2. The endpoint that receives and renders the modal.
  3. The endpoint that receives and responds with a user-specific response.

If you’re a backend developer, implementing these steps will get you the intended behavior, and it’s all straightforward. Let’s walk through implementing each.

The Modal Trigger

Modals need a trigger and a target. When our user clicks the trigger, we want to request the server to generate and return our modal HTML.

Let’s start with our button and what HTMX attributes we’ll need. I use ASP.NET Core Razor Pages, which works with any backend technology. Also, remember some of these attributes are part of the HTMX.NET library and the tag helpers it provides.

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <button hx-get hx-page="Index" 
            hx-page-handler="Modal"
            hx-target="#modal-container">
        Show Modal
    </button>
</div>

<div id="modal-container"></div>

The Razor Pages handler is a simple endpoint that returns an IActionResult.

public IActionResult OnGetModal()
{
    return Partial("Modal");
}

Now that we have that part let’s look at the modal HTML itself.

The Server-rendered Modal

The modal is your run-of-the-mill Bootstrap modal but with some additional JavaScript. Here is the trick. HTMX will execute any <script> elements immediately, allowing you to perform the necessary setup for HTML. The behavior will enable you to use HTMX and the JavaScript the Bootstrap development team intended.

@model IndexModel

<div id="my-modal" class="modal fade" tabindex="-1">
    <div class="modal-dialog modal-dialog-centered">
        <div id="modal-body" class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">From The Server</h5>
                <button type="button"
                        class="btn-close"
                        data-bs-dismiss="modal"
                        aria-label="Close">
                </button>
            </div>
            <div class="modal-body">
                <form id="myForm" hx-post
                      hx-page="index"
                      hx-page-handler="Modal"
                      hx-target="closest .modal-body">
                    <div class="mb-3">
                        <label asp-for="Message" class="form-label"></label>
                        <input asp-for="Message" class="form-control" placeholder="Your message...">
                    </div>
                    @Html.AntiForgeryToken()
                </form>
            </div>
            <div class="modal-footer">
                <button form="myForm"
                        type="submit"
                        class="btn btn-primary">
                    Save changes
                </button>
            </div>
        </div>
    </div>
</div>
<script>
    function showModal() {
        const modal = new bootstrap.Modal('#my-modal');
        modal.show();
    }
    // scopes the modal so we can keep creating them
    showModal();
</script>

Looking through the HTML, you’ll see HTMX attributes sprinkled throughout. HTMX will process these attributes as it adds the HTML clientside DOM. I’m using HTMX’s hx-target to tell HTMX where the following response should go. Please read the HTMX documentation to understand how to use hx attributes for your solutions.

If you have a message-only modal, you can stop here, but if you need modals for further interactions, the next section will show you how to handle that use case.

OK, one more step: we’ll have an interactive HTMX-powered modal.

The Form Submission

We must receive the HTML form’s input elements in our Razor Pages handler. In the sample above, that is a single Message text box. Our Razor Page implementation has an endpoint and a string property.

[BindProperty]
public string? Message { get; set; }

public IActionResult OnPostModal()
{
    return Partial("Success", this);
}

Along with the C# code, we have a corresponding view of Success.

@model IndexModel

<strong>You Said: "@Model.Message"</strong>

Wow, that’s it.

If you’d like to see a complete working version of this solution, you can get the working HTMX Bootstrap modal sample on my GitHub Repository here.

Conclusion

Bootstrap is a popular frontend library designed to allow you to bring any client-side libraries to the party. In my case, I enjoy using HTMX to enhance an otherwise static experience. As you can see in the sample, it only takes some basic problem-solving skills to bring the two together.

I hope you found this blog post helpful, and thank you for reading.