Progressive Enhancement with HTMX

Or what happens when there is no JavaScript

By Paul Hempel lambdaschmiede GmbH

Why Progressive Enhancement

Part 1

Rule of Least Power

[...] suggests choosing the least powerful language suitable for a given purpose.

[range from declarative to procedural]

- W3C Tim Berners-Lee, Noah Mendelsohn

What is Progressive Enhancement?

  • 🔍 User visits website
  • 📱 Browser has JavaScript issues
  • 📖 You can still read and have basic interactions
  • 🖥️ You can switch to a working browser for better UX

Progressive enhancement (PE) is a design philosophy that provides a baseline of essential content and functionality to as many users as possible, while delivering the best possible experience only to users of the most modern browsers that can run all the required code.

- MDN

1% has no (working) JavaScript

  • 🥷🏽 Privacy focused users
  • 📠 Outdated browsers
  • 🛠️ (Broken) Browser Add-Ons
  • ⚙️ Search Engines
  • ⬇️ Error on download (unstable connections)

Why HTMX?

  • 💡 Simple → easy to understand
  • 🚀 Hyped (aka. active project and user base)
  • 🌐 Framework-agnostic
  • 🔨 No front-end build steps
  • 👨🏽‍💻 I know how to use it

Hypermedia as the Engine of Application State (HATEOAS)

A REST client needs little to no prior knowledge about how to interact with an application or server beyond a generic understanding of hypermedia.

- HTMX essays

SQL → Backend → JSON →
HTTP →
JavaScript Templating → HTML

SQL → Backend → HTML →
HTTP


Welcome to my online shop

Part 2

mockup of an online shop with search, filter, products, account page and cart

Level of Interactivity



Low

High

5/10

☝🏽

5/10

hx-boost


<body hx-boost="true">
<a href="/product/some-id">Generic Product</a>

hx-boost

  • 🫁 Only replace the body of the page
  • ⏳ Wait until the new page has fully loaded
  • ⚡️ No flickering
  • ✅️ Graceful degradation = progressive enhancement

hx-put & friends


<button hx-put="/api/product/cart/some-id">
    Add to Cart
</button>

hx-put & friends

  • ❌ Only works with JS != progressive enhancement
  • ⚙️ Extends limited form+post requests

hx-put & friends


<form action="/api/product/cart/some-id/add">
    <button type="submit">Add to Cart</button>
</form>

We need to fall back to form requests!

hx-put & friends

  • ✅ Will always work without JavaScript
  • 🆗 Put/Delete/... needs to be baked into endpoint
  • 🆗 Can be abstracted with templating

Delete button templating example

const delete_btn = (label, url) =>
  `<form action="${url}/delete>
        <button type=submit>${label}</button>
   </form>`

hx-trigger

<input name="q"
       hx-get="/search"
       hx-trigger="keyup changed delay:1s"       
       hx-target="#search-results"/>

hx-trigger

<form action="/search">
  <input .../>
  <noscript>
    <button type="submit">Submit</button>
  </noscript>
</form>

Progressive Enhancement in the Backend

Part 3

Detecting HTMX

const search_handler = (request) =>
    request.headers["hx-request"]
        ? render_search_results()
        : render_full_page()

hx-swap

<a
    href="/products/page/2"
    hx-get="/htmx/products/page/2"
    hx-swap="outerHTML"
    hx-target="main"
    hx-push-url="/products/page/2">
  Next page</a>

hx-swap

  • Replaces only a section of your page
  • Backend can decide how to respond
  • Makes reactive & responsive websites possible, just like SPAs

Deciding in the Backend

  • You can always check for hx-request
  • Always indicates if JavaScript is available
  • You can decide to add/remove parts of the page

Deciding what to show

const some_component = (has_js) =>
    has_js
        ? `<MyWebComponent />`
        : `<div>no web component alternative</div>`

Web Components

  • ⚙️ Requires JavaScript to work
  • 🚀 Significant UX improvement possible
  • 📄 form as fallback option

First page load

  • You can never check for hx-request
  • You don't know your clients capabilities
  • → Important limit to know

Deferred Loading

<div hx-get="/graph" hx-trigger="load">
  <img alt="Loading..." src="/spinner.svg"/>
</div>

Deferred Loading

  • Does not work, if JavaScript is missing
  • "First page load problem" applies
  • Can be used on subsequent hx-swaps
  • Increases response time, if some requested elements take more time to load

PE without HTMX

Part 4

screenshot of a time tracking website with clickable day tiles
screenshot of a time tracking website with enhanced date range selection

You can still use JavaScript

  • Progressive Enhancement always possible
  • A few lines of vanilla JS can get you a long way
  • You don't need React
  • Still no compile step

Progressive Web Apps

  • Works with MPA
  • Use web workers to cache REST calls

What's next

Part 5

Accessibility side effect

  • 📖 No JavaScript promotes use of semantic HTML
  • ♿️ Native HTML elements are already accessible
  • 🔗 URL represents state and is sharable

DX side effects

  • Can reduce LoC by 60 to 70%
  • Makes you a more fullstack developer
  • Removes split between FE and BE only devs
  • Easier testing (no hidden state)
  • API is always in sync

But does it scale?

  • Only one service to scale horizontally
  • Still stateless
  • Easy vertical scaling
  • Micro-frontends still possible

But animations?!

  • View Transition API works with MPA
  • CSS3 animations are great and easy to use

When evaluating your framework do not only compare SPA options. Also include simpler approaches, like HTMX, and compare the additional benefits, like PE, for your users.

Thank you for listening!
Questions?

Talk, Handout & Contact

lambdaschmiede.com/talks

qr code for lambdaschmiede.com/talks