htmx on Sinatra

—interactive web pages with hypermedia

Part 3: Instant Gratification

Time to take hmtx on a ride. First off, we must import the library using any of the installation instructions listed on the documentation page. htmx is imported using the script tag, it has no build step, and no dependencies. What a breath of fresh air!

Here's how htmx works. In the current implementation of HTML, only links and forms can trigger actions on the server. We can override this limitation by giving an arbitrary DOM element an attribute from the library's core set:

  • hx-get
  • hx-post
  • hx-put
  • hx-patch
  • hx-delete.

As their names suggest, these attributes map directly to HTTP verbs. Once augmented with one of the attributes above, a DOM element is able to communicate with the server independently through AJAX.

hx-get

For example, we can make a button issue GET requests:

<button hx-get="/hello">
  <span> Submit &rarr; </span>
</button>

hx-get: issues a GET request to the route specified in the attribute value. On the back-end, the following Sinatra route is the target of hx-get="/hello":

get '/hello' do
  'Hello World!'
end

Note that in this example, we are requesting data from the server; this is equivalent to the behaviour of the a (link) element. When creating a new entity on the server (the domain of the form element), we would use POST instead of GET.

The server response will be inserted into the DOM based on the value of two attributes: hx-swap and hx-target.

hx-swap

hx-swap tells the library how the response must be inserted in the DOM:

<button hx-get="/hello" hx-swap="outerHTML">
  <span> Submit &rarr; </span>
</button>

Setting the value of the attribute to outerHTML will insert the server response in place of the target element (here, the button itself) starting from the outermost tag. This will effectively replace it in the DOM. If, instead, we change the attribute value to innerHTML, the response will be nested inside the target tag at the level of first child.

When hx-swap isn't specified, it defaults to innerHTML.

Currently, the button is both the issuer and the target of AJAX requests. What if we want to insert the response somewhere else in the DOM?

hx-target

hx-target tells the library where the response must be inserted in the DOM. We can give an element the id attribute, and then access it from another element with hx-target:

<button hx-get="/hello" hx-swap="outerHTML" hx-target="#greetme">
  <span> Submit &rarr; </span>
</button>

<p id="greetme">Greet Me</p>

This will replace the targeted element with the response.

Special effects

We can enhance our elements with transition effects by applying CSS animations. Here's the CSS:

.fade-in {
  opacity: 1;
  animation-name: fadeIn;
  animation-iteration-count: 1;
  animation-timing-function: ease-in;
  animation-duration: 1s;
}

@keyframes fadeIn {
  0%   { opacity: 0; }
  100% { opacity: 1; }
}

And here's what our app.rb must now return:

get '/hello' do
  "

Hello World!

" end