HN user

deniz-a

59 karma

HTML expert

Show HN is the only good thing about this site

I work for Big Sky Software and Commspace

https://denizaksimsek.com

Posts3
Comments31
View on HN
The Future of Htmx 2 years ago

The server-rendered web app (without htmx, think crusty PHP router admin panel) is actually very similar to the Elm architecture.

The database is the Model.

The server is the update function.

The HTML template is the view function.

HTTP requests are the Msg.

Database/API calls are the Cmd.

The problem with this model is that recreating the whole UI every time is slow and clunky.

Elm and most JS frameworks fix this by running the `Model` and `update` function on the user device and doing tree diffing. This is reasonably performant if you do it well, but it's also hard to do well (see: every website ever these days) and introduces complexity, particularly because the client-side `Model` almost always needs to be synchronized with a database or some other "canonical" model.

As pragme_x touched on in a sibling comment, what htmx does is not an iteration on the framework model; it's an alternate solution to the original problem. What it does to avoid the clunkiness is let the server to return `Delta HTML` instead of `HTML`, and for the slowness, it leaves the vanilla-JS escape hatch open for things that can be done without altering the Model (database). This model also has its flaws, especially if you need to use that escape hatch a lot, but it's also a lot simpler conceptually and keeps the `Model` and `update` function in a trusted computer, meaning you don't need to marshal and validate stuff between two models.

how to write good Htmx code that wouldn't bloat and rot over time?

- Don't try to perform fine-grained updates if you don't have to. It's fine to replace a little more than you absolutely need to, and htmx takes care of some of the things you might worry about like preserving focus. For example, in the "delete row" example, it might be more robust to replace the whole table.

- Use htmx for anything "side-effecty", i.e. anything where a network request is compulsory. Relegate JavaScript to "micro-interactions". I consider a code smell in an htmx application any time I need to add new elements to the DOM via JavaScript, most of it should be manipulating classes, attributes and styles.

- For those micro-interactions, the "component" model heralded by the big three JS frameworks doesn't work as well here, since components have their own state, which is another "shadow-Model". Instead, try the RSJS methodology (https://rstacruz.github.io/rsjs/) with "behaviors".

- To wire up that JS to your app, reach for custom events. The `hx-trigger` attribute is the "port" that translates JS-based interactions to Msg.

I suspect this is because HTML was always about documents, and never about interactive applications

This is a really common refrain and it peeves me, but I don't have a good rebuttal to it yet so I won't weaken my comment by trying to make one. I don't have good explanations for a lot of things about htmx and hypermedia, actually. The code of htmx is mostly complete, but the theory is still WIP.

Co-author here. We rewrote the book in Typst. On the hypermedia.systems website, the new EPUB ebook release is built from the new codebase. I'm working on a blog post with details on why we switched and what the process was like. The content of the book has not meaningfully changed.

If users are expected to spend a long time and use many tools to compose something, then I agree a full page form is best. For something like adding to a todo list, or where users are expected to add many items one after the other, a modal is better.

You can also take a hybrid approach: many calendar apps have a small popup for adding an event that the user can expand into a whole (nonmodal) window for editing all the details.

In the sample code, there is no "streaming" going on -- the server simply uses the client code as a template to generate HTML and sends it as a normal HTTP response. In pseudocode:

    import "client.js" as client
    on request:
      document = new ServerDOM(),
      client.render(document, data),
      respond with document.toHtmlString()

> I don't understand why it isn't "true" SSR This article seems to be using the term SSR exclusively in the frontend framework sense, where client code is run on the server. It's not how I use the term but it is a common usage.

Another possible reason that the htmx approach isn't discussed: the any-server-you-want nature of htmx is terrible for selling Deno Deploy :]

Hypermedia Clients 3 years ago

If you're writing a web app that only works in a browser you control, then no, you won't benefit from hypermedia. The rest of us like search engines being able to index our sites though.

Hypermedia Systems 4 years ago

1. The best way to avoid creating an explosion of states is [Locality of Behavior]. Elements should avoid targeting other elements that are not inside them, especially those written in different templates. Sometimes this is unavoidable (i.e. updating a site-wide shopping cart). If htmx is adopted widely and those state explosions become an issue (I'm not aware of it happening so far), maybe a "redux for htmx" will be invented. 2. "Classic HTTP and HTML are all about serving static files," I'm not sure about that. In my opinion, dynamically generated responses are a core part of HTTP. All HTML and all the htmx attributes are sent by the server, so we're only keeping the server in sync with itself. I get what you mean though. Going back to the shopping cart example, the code that adds an item to the cart needs to remember that the cart total is displayed in the nav bar and include that fragment. A server mechanism could possibly be created that tracks these dependencies, but again, it's not been an issue yet.

Hypermedia Systems 4 years ago

If I was to test things that were part of UI code, but were still "mechanical" enough to test automatically, I would throw responses into an HTML parser to make sure I'm presenting the right data. (In a way, HTML strings are also a kind of "virtual DOM")! If you need to test interactions though, I don't think a headless browser is avoidable.

Hypermedia Systems 4 years ago

Hyperview only uses React Native for its cross-platform widgets. (I wish Flutter had been used instead). You don't need to touch it unless you're writing custom elements.

Hypermedia Systems 4 years ago

The book segment header links (in italic [0]) serve raw html

Fixed! (It worked fine in the dev server...)

The way I think about it is htmx takes the "full page refresh" user experience & programming model, and expands it to "partial page refresh" and goes from there. It also patches some annoying gaps in html, like forms only being able to GET and POST. Finally, it lets you trigger these requests with things other than clicking a link/submitting a form.

LiveView tries to replicate the architecture of a native app toolkit and slap a network boundary in the middle of it, whereas htmx tries to keep the programming model of Web 1.0 apps while radically expanding the user experience possibilities

Most of what htmx does is a direct extension of the browser's capabilities. Some issues I can think of are

- maintaining the "accessible by default" nature of HTML

- swapping. htmx takes html from the server and uses one of innerHTML, outerHTML and insertAdjacentHTML to put it into the document, but it also does a whole lot more to avoid unexpected behavior. This would all need to be specified and implemented natively

The pattern is that you use htmx for anything it can do, then use Alpine for things that shouldn't/can't require a server round-trip (toggling sidebar, animations etc).

You can reimplement htmx functionality with Alpine, but htmx is a lot better than `@click="fetch('/partial').then(res => res.text()).then(frag => $el.innerHTML = frag)"` all over the place (with a slight variation in that one component that you miss because of the noise).

Don't use _hyperscript just to be "'`consistent`'", use it because you want to. Being agnostic and playing well with other tools is a big thing for htmx. _hyperscript is lots of fun and lets you write very readable code, but you also need to stomach a 26mb bundle just to toggle some classes :/

(if anyone reading this can help us make it smaller, LMK)

API returning [...] HTML

The endpoints that return endpoints are not API endpoints, they are UI endpoints like `/index.html` or `/login` or `/Results.aspx?id=3984272`. Now you can have `/login/email` or `/login/google` and switch between them like <https://htmx.org/examples/tabs-hateoas/>.

As for the machine-consumed API, you have quite a few options. e.g.: The server that serves these resources may serve JSON to the same URLs through content negotiation, or the API may be separate, or you can have a generic API that is consumed by mobile app, desktop app and HTML-producing server (as opposed to JS app delivered by static file server or framework SSR).

The UI runs on the server, using REST to change client state over a stateless protocol similarly to how a React app may use Redux.

You don't return HTML from API endpoints, you return it from your UI endpoints. You could reuse those for JSON with content negotiation, or have a frontend server+backend server setup where the backend server is also a public API, or some other solution.

Thankfully, we have arithmetic expressions :]

Every command has an initial verb, which makes parsing easier (both by computer, and hopefully by people). Just for fun, here are your examples in hyperscript:

    get numA / numB then put it into resDiv
    -- or simply
    set resDiv to numA / numB
    
    repeat 5 times index loopCount
      log loopCount
    end

For a trivial example like fading out, you're right, but if you want to make dynamically generated animations, hyperscript's features are very helpful (The Web Animations API is a boon, but browser support isn't there yet).

Separation of HTML/CSS/JS doesn't always mean separation of concerns. Every application has a different definition of a "concern", and if it doesn't align with the separation between languages, you don't get decoupled code, you intensify the symptoms of tight coupling.

You're pretty much right. Modifying the code at runtime is useful for devtools fun, but not much else.

If you'd like to play around, open the console and try calling the `_hyperscript` function:

    _hyperscript(`get the first <button/> 
                  transition its background-color to 'red' over 2s 
                  log ':]'
    `)