htmx on Sinatra

—interactive web pages with hypermedia

Part 7: Enter htmx

It's time to enable htmx in our views. First, we need to import the library in our layout file:

<script src="https://unpkg.com/htmx.org@2.0.0/dist/htmx.min.js"></script>

And with that, we're ready to go. We submit the form, and lo and behold! Our "thank you" message is right there, inserted into the layout. Two things are immediately noticeable, however:

  1. the form is still visible — only the button has been swapped;
  2. the inserted message does not align with the rest of the design.

But there is an even more insidious problem. Let's try to submit an empty form. We clear the input fields, and press the Submit button.

Oops! We were able to bypass input validation and submit a form without parameters.

As good web standards developers, we had made sure to include client-side input validation — by way of the required attribute among others:

<input name="name" type="text" maxlength="255" tabindex="0" placeholder="Your Name" required>

However in this current implementation, the validation appears to be de-activated.

Let's fix the problems, one by one.

Fixing input validation

Note that right now, we still have our form element with the action and method attributes declared on it:

<form action="/" method="post">

It is the Sinatra way of submitting forms, and it works. However, now we also have htmx in the mix — as a leftover from our "instant gratification" example:

<button type="submit" hx-post="/" hx-swap="outerHTML">

There's obvious duplication here. Both the form element and the button are trying to do the same thing: POST to an URL.

It turns out that when a form is present, htmx expects the hx- attribite to be declared on the form element, not the interactive element (the button). We make the following changes to our markup:

<form hx-post="/">

<button type="submit" hx-swap="outerHTML">

We refresh the page, and submit the form again.

  • form validation now works as intended
  • the "thank-you" message now replaces the entire form.

Fixing the layout

The layout is still skewed. In order to troubleshoot it, we inspect the resulting HTML using the browser developer tools — and right away, we notice some interesting stuff.

We have two main tags now — the one from the target document (index.erb) and one from the injected document (thank-you.erb).

Remember that our "thank-you" message lives in its own template, thank-you.erb. This was necessary during the previous steps, because we were issuing a dedicated page request to the server every time we wanted to display the message:

<body class="viewport-centered text-centered">
  <main>
   <div class="check">&#10004;</div>
   <h2 class="no-margin">Thank you, <%= name %>!</h2>
   <p class="no-margin">We'll be in touch when we launch.</p>
  </main>
</body>

So it appears that when htmx must insert a DOM subtree into another document, it does the following:

  • Omit the body tag from the subtree if it is present
  • Insert the subtree beginning with the body tag's first child, (main in our case).

This is logical: htmx gives us the ability to do local page updates, so it wouldn't make sense to insert a page that starts with the root element (the body tag) into another page.

This means we must refactor the thank-you.erb template, omitting and renaming elements and re-applying the classes:

<div class="text-centered">
 <div class="check">&#10004;</div>
 <h2 class="no-margin">Thank you, <%= name %>!</h2>
 <p class="no-margin">We'll be in touch when we launch.</p>
</div>

And, look at that! Now that the correct styles have been applied to the inserted subtree, the layout works just as intended.

Transclusion

So here's what htmx enables us to do:

  1. we click a button
  2. the library makes a request to the server
  3. the server sends back a snippet of HTML
  4. the library inserts the snippet into the document, maybe replacing a DOM element that we had specified.

This is behavior formerly known as AJAX; however, AJAX is merely an achronym for the technologies involved in doing partial page updates.

The proper name for this phenomenon is transclusion.

It's the idea that hypermedia (of which HTML is the most widespread implementation) can include pieces of other hypermedia via reference — and without needing to reload the entire document in the browser.