Hacker News Re-Imagined

Django, HTMX and Alpine.js: Modern websites, JavaScript optional

  • 449 points
  • 7 days ago

  • @czue
  • Created a post
  • • 269 comments

Django, HTMX and Alpine.js: Modern websites, JavaScript optional


@jamesgeck0 7 days

Replying to @czue 🎙

It's worth noting the security tradeoffs of these micro-frameworks.

HTMLX uses innerHTML/outerHTML extensively, meaning that XSS is a real concern. Any sanitation of user-generated content must happen server-side. This how traditional server-side frameworks generally work, but it's the opposite of how sanitation tends to be handled in large JS frameworks such as Angular.

https://htmx.org/docs/#security

Alpine.js requires an alternative syntax to avoid running afoul of unsafe-eval Content-Security Policy. With this more verbose syntax, there no inline expressions in templates; everything is bound to attributes in Alpine.data instead.

https://alpinejs.dev/advanced/csp

Reply


@btown 7 days

Replying to @czue 🎙

An alternative that requires even less server-side work is to use PJAX https://www.npmjs.com/package/pjax which is the spiritual successor to Turbolinks from the early Rails days. It's really a two-step process:

- create a quick JS file that loads and configures PJAX to automatically intercept links and - wherever you would have $(function() {}) ensure that it's also triggered on the PJAX load event, and is idempotent (e.g. check if you initialized a component before initializing it again)

Then you just render pages server-side like it's 2000, without any pjax-specific code needed in your templates themselves.

If you're building a data-focused product either solo or with a small team, it allows you to focus on your Python code, and anything frontend is as easy as accessing the variable in a Django template. Of course, it's a massive accumulation of technical debt - when you're successful you'll need to rewrite in a modern framework for maintainability and agility. But when you need to "punch above your weight class" and just get a product out into the world, it can be an amazing technique.

Reply


@joelbluminator 7 days

Replying to @czue 🎙

I like how many frameworks end up cloning what Rails is doing (Stimulus / Turbolinks etc).

Reply


@cosmotic 7 days

Replying to @czue 🎙

What makes it 'modern'?

Reply


@ilrwbwrkhv 6 days

Replying to @czue 🎙

Glad to see just a simple cdn script tag back in the play. Hopefully we can also pivot to smart documents rather than the web being an application platform.

Reply


@joseferben 7 days

Replying to @czue 🎙

I wrote about my experience evaluating the same stack here: https://www.joseferben.com/posts/hoarddit_a_website_to_disco...

Reply


@miki_tyler 7 days

Replying to @czue 🎙

This is a very promising architecture! We are actually building a template for Kit55 (http://stack55.com) that illustrates the use of Alpine,js. I think we should look into HTMX as well. Do you know if there is a comparison of Alpine.js vs HTMX somewhere?

Reply


@awinter-py 7 days

Replying to @czue 🎙

'django saas startup kit' concept of the author is really smart

there's a hole in the market for people wanting to use frameworks to make saas sites -- frameworks provide great primitives that are subtly unusable for some modern web apps. Login but not saml, admin but not multitenant, forms but not multiple forms on a page

feels like this scratches an increasingly common itch of 'the web as a platform isn't shaped like what people use the web for'

Reply


@jpe90 7 days

Replying to @czue 🎙

Any opinions on how this (or a PETAL stack) compares to using Clojure & Clojurescript?

Reply


@Mizza 7 days

Replying to @czue 🎙

I've been moving from Django to Elixir/Phoenix/LiveView and loving it so far. I hated the Angular/React era of the web and mostly moved to doing backend development, but the Live App era has reinvigorated my interest in web development as a whole. I'll miss Python a lot and hope they can come up with something that solves the concurrency issues, but Elixir is really pretty good in its own right.

Reply


@amenod 7 days

Replying to @czue 🎙

I agree that the way modern JS (need to?) download half the Internet for a semi-useful app is annoying, wasteful and dangerous.

However, I still remember the hell that was updating the state and keeping the rendering in sync with it when using jQuery. Native JS of course doesn't improve on that, and (judging by the description - no experience with it) Alpine.js doesn't either. For me, the paradigm of rendering from the internal state is what makes React (and Vue,...) useful. Too bad that npm ecosystem sucks and that everyone is inventing new shiny useless new things all the time... But React makes non-trivial single-page apps possible.

Of course, if you need server-side rendering (for example because of SEO) then the equation changes, and maybe using Alpine.js makes sense.

Reply


@gavinray 7 days

Replying to @czue 🎙

Can someone genuinely explain to the the desire/interest to use HTMX/Hotwire/LiveView etc?

I was writing web apps when rendering templated HTML views on your server was standard, and you had controller endpoints that returned HTML content, or things like Rails "Unobtrusive Javascript" where you had code like:

  $(‘#user-list’).html(“<%= j render(‘index’, users: @users) %>”)
As an automatic callback to some action.

Whether or not you love or hate this model of development is irrelevant, because if at some point you need to render on a client other than a browser -- you have to start from the beginning and write an API now.

IE, so that your Android/iOS app in Java/Swift or whatnot, can ask your backend for data and render it using the tools available on that platform.

When we turned apps into pure API's and just exchanged data with them, it opened up the ability to be platform-agnostic.

I guess I don't understand why someone would choose to build an app that sends HTML over the wire instead of JSON/XML/whatever, that any client can render. All it means is that you have to write this functionality later when you want it, in addition to now having this HTML stuff.

Reply


@bingohbangoh 7 days

Replying to @czue 🎙

I'm a big fan of these setups but has anybody ever successfully used a minimal-JS setup for anything useful?

I mean, there's apps like Pinboard but React & Redux do handle a lot of complexity and most modern apps demand that complexity.

Reply


@streamofdigits 7 days

Replying to @czue 🎙

would it possible to have a browser ship with htmx so that it is possible to have truly "javascript optional"? (in principle this applies to any full js framework but I suppose it is natural in the htmx context)

are there any unsurmountable issues around security and the sandbox etc (not terribly familiar with browser internals)

Reply


@pjs_ 7 days

Replying to @czue 🎙

HTMX really kicks ass, I simply love it.

Reply


@chrisfinazzo 7 days

Replying to @czue 🎙

Smells like Turbo (and related frameworks) to me, which isn't a bad thing. From my brief use of CRA, I can see why many people are attracted to it, but the fact that a bunch of JS has to download before anything happens is never far from my mind.

In the meantime, I'll keep waiting until we get native HTML imports/includes - hopefully before the heat death of the Universe.

Reply


@Savageman 7 days

Replying to @czue 🎙

It reminds me a bit of [MooTools Behavior](https://mootools.net/blog/2011/12/20/mootools-behavior) (from 2011!) where interactivity was created by adding tags to HTML, similar to the article!

Reply


@synergy20 7 days

Replying to @czue 🎙

If I use alpine.js why do I need htmx still? I would use either of them but not both. Using both seems making a supposed-to-be-simple approach immediately back to complicated-again-now-you-need-two-components.

alpine increases client side interactivity, it can also do ajax to talk with server, why do I still need htmx then?

On the other hand if I use htmx I will probably use its hyperscript for interactivity on the client side, to be 'consistent'.

Note both projects are trying to make frontend simpler, mixing them seems not to help that goal to me.

Reply


@nawgz 7 days

Replying to @czue 🎙

I take so much issue with HTMX being branded as "JavaScript optional". It's a project built for people who hate JS by people who hate JS, and as the resulting developer workflows aren't JS-based have managed to convince themselves they're not using JS.

It's totally false. Your HTMX app does not work for JS-disabled people any more than someone's React/Angular/Vue first-render-on-server app.

Reply


@colbyhub 7 days

Replying to @czue 🎙

Another one to consider is https://www.django-unicorn.com if you want that LiveView feeling for Django.

For my latest project[1], I've opted for https://unpoly.com instead of Alipine+htmx as Unpoly allows me to write 100% server-side code and sprinkle some progressive enhancement where desirable. As a result, I can offer a no-JS experience (minus the Stripe Checkout) for those who may want/need it. Additionally, it forces me to focus solely on the server-side, and I write more idiomatic Django code as a result.

[1]: https://heraldsms.com

Reply


@sbussard 7 days

Replying to @czue 🎙

I always have the fear that something I write will be too complicated for someone else. This has become particularly irksome when integrating OIDC into a single page app. It’s gut wrenching to double check every random header for every domain it needs to serve, to set up SSL for localhost, to include the right headers on the fetch request, and maybe edit the /etc/hosts file to make it work. That process is a joy killer. The tools mentioned here can help stop that madness and bring joy back into development. So thank you! Please evangelize these ideas to browser standards committees and whoever will listen.

Reply


@polyrand 7 days

Replying to @czue 🎙

After reading some HTMX criticism, there's one point people seem to miss. Making HTML your application interface does *not* prevent you from having a JSON (or something else) API. If anything, it probably forces you to split your functions better. e.g:

  def get_user_data(user_id: int) -> Dict (JSON-looking data):
      ...

  def render_user_view(user_id: int) -> HTML:
      user_data = get_user_data(user_id)
      render_template("user_detail_view.html", context=user_data)
If you need the user data in a JSON API, nothing prevents you from exposing `get_user_data` as a JSON endpoint. You can also use WebViews in a mobile app.

People tend to overestimate the "interactivity" needs of their apps and underestimate what they can achieve by just swapping HTML. HTMX also lets you swap "Out of Band" [0]. This makes it easy to model more complex interactions (like "reactions"), for example, updating a counter somewhere else in the app when a form is submitted. Reactive frameworks can also become a Rube Goldberg machine if an app is not properly designed from the beginning. Then you start fighting rendering loops, build dependencies, components' side effects, etc.

Personally speaking, HTML-driven apps are not just about easy vs. hard development, it's also about your users [1]. Maybe a big React app runs fine on 8 CPU cores and 32 GB of RAM, but very often, your users just want to read some content, maybe submit a few forms, and leave. They may not want to download 2 MB of JS so that the page can render boxes with text, even more if your browser can already do that if you give it some HTML.

[0] https://htmx.org/attributes/hx-swap-oob/ [1] https://shkspr.mobi/blog/2021/01/the-unreasonable-effectiven...

Reply


@nop_slide 7 days

Replying to @czue 🎙

Can anyone describe the use cases between HTMX/Alpine and Unpoly JS? I see the 3 routinely mentioned, but I am unsure what coverage Unpoly has vs HTMX/Alpine.

https://unpoly.com/

Reply


@thih9 7 days

Replying to @czue 🎙

Is anyone here using either HTMX or Alpine.js (or both) in production? What are your thoughts, are you happy with how these work in practice?

Reply


@strogonoff 7 days

Replying to @czue 🎙

Downside of most HTMX examples (such as the form in TFA) seems to be lack of graceful degradation with non-JS-enabled user agents.

But then, the rest of the page is server-generated (taking care of SEO), and handling interactivity without JS may not be a priority for most sites nowadays.

Reply


@abishekg 7 days

Replying to @czue 🎙

This is a pretty cool idea. The only catch is retaining consistent state across the page when there are multiple components that depend on same data. For instance, if there’s a form that submits to increment or decrement some dataset, but there is a stat based on that data in the sidebar, then it’s nearly impossible to keep the stat relevant. That said, its a planning problem than a stack problem.

I’ve been trying this in all my hobby projects and am absolutely hooked. It feels simpler than even django-sockpuppet, say.

Reply


@theptip 6 days

Replying to @czue 🎙

Based on the content of the last DjangoCon, it seems HTMX is the way the Django community is going.

Anyone have thoughts on HTMX vs django-reactor (which is a LiveView port)?

Reply


@beebeepka 7 days

Replying to @czue 🎙

I think it's great people who passionately dislike JavaScript have such powerful options.

Personally, I am not buying the whole "you can do anything this way" because it seems to me the main driver, implied or plainly stated, is always the "and no js!" part.

I get it, though. We are all capable of going to great lengths to prove a point. Having more viable options is great.

Reply


@1270018080 7 days

Replying to @czue 🎙

This honestly sounds like a miserable tech stack.

Reply


@Wronnay 7 days

Replying to @czue 🎙



@ksec 7 days

Replying to @czue 🎙

Sorry if this is off topic, I remember reading a proposal of including something similar to Alpine.js / HTMX within the HTML5 spec.

But I lost the page. And the proposal itself doesn't mention Alpine.js or HTMX so no keyword bring it up in Google. I am wondering if anyone have any idea.

Reply


@resoluteteeth 7 days

Replying to @czue 🎙

One major downside to HTMX is that as soon as there is any state you have to keep track of, you're teleported back to the late 90s or early 2000s where you have to keep track of all your state in hidden form fields, and you're extremely constrained in how you update pages.

IMO, it's annoying enough that it's probably not worth it unless you're doing something trivial. If you want to render html on the server you can still do that, but in many cases it is easier to just use custom javascript and maybe even receive the rendered html as json to simply updating pages rather than use htmx.

Reply


@austincheney 7 days

Replying to @czue 🎙

I’m not sure I understand the motivation to avoid JavaScript. I completely understand not wanting 400mb of SPA/NPM nonsense, but otherwise what problem does avoiding JS solve in a webpage?

* Is it because JavaScript is too hard to learn or too unpleasant to write?

* Is it because training people to write JavaScript is too much effort?

* Is it due to a lack of trust in your fellow developers?

* Is it due to challenges in planning or forming architecture?

* Something else?

Reply


@sam_goody 6 days

Replying to @czue 🎙

There is a cycle that has existed for a while:

- Home computer.

- Mainframe. Much more powerful and reliable.

- More powerful PC. Mainframes have latency et all.

- More powerful Server, browser cannot handle that much computing.

- More powerful browser. A whole OS really. Servers have latency.

- More powerful server.. All that complexity is hard to rebuild on the client side, and besides phones are too slow...

Party on!

:D

Reply


@jonatron 7 days

Replying to @czue 🎙

Not sure about the snippet:

    // when the DOM is loaded
    document.addEventListener('DOMContentLoaded', () => {
      // find every element with the class "close"
      (document.querySelectorAll('.close') || []).forEach((closeButton) => {
        const parent = closeButton.parentNode;
        // and add a "click" event listener
        closeButton.addEventListener('click', () => {
          // that removes the button's parent from the DOM 
          parent.parentNode.removeChild(parent);
        });
      });
    });
It'd be clearer without useless comments and useless extra code:

    document.addEventListener('DOMContentLoaded', () => {
        document.querySelectorAll('.close').forEach((closeButton) => {
            closeButton.addEventListener('click', (event) => {
                event.target.parentNode.remove()
            })
        });
    });

Reply


@robertoandred 7 days

Replying to @czue 🎙

Don't use a framework, use three!

This sounds like a lot of effort just to brag about how you avoided whatever piece of tech we're considering to be evil this month.

Reply


@jonathan-adly 7 days

Replying to @czue 🎙

For people who want to see more advanced examples/tutorials built using Django and htmx- you can see a bunch here: https://htmx-django.com/

Anything that can be done with React/Vue can be done with htmx in a more “Django-way”- it’s an amazing library that allows for complete web applications without the complexity of the JS ecosystem

Reply


@reidjs 7 days

Replying to @czue 🎙

Can this stack easily handle 2-way data bindings on the frontend? For example, when you update a text input, the title of the text input changes?

Reply


@fareesh 7 days

Replying to @czue 🎙

A suggestion for folks writing articles like this - sell me the advanced but typical example. I want to see an image input which is then previewed and then uploaded asynchronously with a loading bar and error states.

Reply


About Us

site design / logo © 2021 Box Piper