Troubleshooting is a Lifestyle 😎 with Jack Linke
Published December 6, 2024
This video features Jack Linke at DjangoCon US 2021 in Online.
With no 'standard' approach to front-end other than built-in templating and so many JS front-end frameworks and tools and options, it's easy to get thrown for a loop. HTMX lets us stick to what Django is good at - server-side stuff, mainly - without feeling like we're missing out on modern tools.
This talk was presented at: https://2021.djangocon.us/talks/server-side-is-dead-long-live-server/
LINKS:
Follow Jack Linke 👇
On Twitter: https://twitter.com/JackDLinke
On GitHub: https://github.com/jacklinke
Website: https://www.jacklinke.com
Follow DjangCon US 👇
https://twitter.com/djangocon
Follow DEFNA 👇
https://twitter.com/defnado
https://www.defna.org/
Video production by the speaker and DjangoCon US 2021 Volunteers.
Jack Linke argues that Django applications can regain the simplicity of traditional server-side development while still providing modern interactivity by using HTMX. He explains how HTMX attributes let HTML trigger requests, replace page fragments, update settings, and load independent forms without adopting a large JavaScript framework or single-page application architecture. Through inbox, instant settings, and tabbed-form examples, he shows how Django views can return either full templates or small HTML fragments, while also covering CSRF handling, view organization, complementary tools such as Hyperscript and Alpine.js, and resources for learning more.
Summarised automatically from the transcript.
Automatically transcribed, so expect mistakes in names and technical terms.
Welcome and thank you for joining me. Hopefully next time we'll get to do this in person. My name is Jack Linky. I've been a Django developer since 2018, so not that long, but uh really dove in headfirst. I am a PSF contributing member. And I'm also the lead developer for a web up application for utilities districts that helps them manage their resources I got involved with HTMX late last year, really kind of at the beginning of its uh development and increasing popularity, so I was very fortunate to uh be involved. My talk today is entitled ServerSide is Dead, Long Live ServerSide plus
HTMX. And thank you again for joining me. So I started web programming at the turn of the millennium, back in the early days of Web 2. 0. JavaScript was still very young. Ajax, which is the ability to asynchronously do things like form submissions or updating parts of the page without needing a full refresh, it really wasn't even a thing yet. CSS still felt really experimental, if you'd even heard of it. But interactivity and dynamic content were were the new thing. And in those days, everything was done server-side, often in Perl or PHP. We were creating really cool database-backed web applications.
uh that felt powerful. It was you know it was something really new. A website wasn't just a static page of text and images. It could now respond to your inquiries and return customized content Sadly, while I continued programming and other technical work, life really took me away from the web programming aspect of things for the most part for about a decade and a half. When I got back into developing for the web in 2018, the whole environment was completely different. Half the internet was running on JavaScript. Separate teams were working on the front end and back end. There was complicated workflows and APIs. And it seemed like there was layer upon layer of frameworks.
to enable that same kind of dynamic interactive website. So in some ways, in 2018 I was really brand new to web development all over again. But I remembered, uh maybe with rose-colored glasses, that web development was in many ways much more straightforward 20 years ago. So it really brought to mind the question, does modern web development have to be super complicated? Hmm. So what's the current state? Well Django provides you with templates. But how do you apply interactivity if you want to do partial page refreshes or submit a form without the entire page reloading? What do you do? And there's a variety of different approaches.
Vanilla JavaScript, you know, you can throw some Ajax inline in your page. jQuery allows you to do some really cool stuff uh but it's kind of outdated now. Single page applications gained a lot of popularity so you have div after div after div in your page You can use view directly, which keeps things a little more lightweight. You know, you just import the view script and you can do stuff. Pretty pretty straightforward. And then there's more heavy things like React or Angular, but so many different directions and a lot of these are really heavy require you to learn a whole lot.
Here's a few of the frameworks that have been developed over the past two decades. And most of the tools in this image are focused at least in part on the front end. You can see Django in there at one point, but most of these are front-end tools, and there's just a ton of them Keep in mind that this image hasn't been updated for two years, so just even in that time, several more have come out. And if you look at this roadmap of front-end development, you'll see an incredible amount of things that a new person really needs to learn. To get all of the effects uh all of the benefits of modern web development.
It's it's just a little crazy Now what's amazing about this image is you can really ignore about half of the boxes in it when you apply HTMX and still get most of the same benefits. So what's the standard approach in Django for modern front-end development? How do we get the interactivity that we want? Well, good question. Django was really created and initially came to popularity at a time when server side was really very much the norm. You did all the development on the server side and you pushed that content to the front. That's really
how Django templates work. Over the past decade, there's been so many single-page application approaches, JavaScript front-end frameworks. uh different tools and options that it really throws those who are new to Django for a loop. And it makes those uh who've been working with Django for a little longer feel like they've Got a bunch of new stuff to figure out. There's really not a standard approach to the front end other than the built-in templating. And many people feel that that approach is a little bit outdated. So, where's the middle ground? Well, one potential approach is HTMX.
The whole reason that we're here talking today. HTMX is really just an extension to existing HTML. So it's not something that requires you learning an entire new language or framework It's back-end agnostic, so you can bring your own backend. It works well with Django, but it also works well with a variety of other backends. Uh fast API, Flask, uh and and in other languages as well. It's focused, it only does a few things. It's really uh you know all about the interactivity uh between the front end and the back end And allows you to access Ajax, CSS transitions, WebSockets, and ServerSend events
all directly in your HTML. You don't have to incorporate a bunch of additional JavaScript files or build out anything crazy. You can do it all right in the HTML. And the nice thing about HTMX is that it's rather small. Once it's G-zipped, it's only about 11 kilobytes. So, how do we get from the traditional Django template approach to a more modern approach with HTMX while sidestepping all of those JavaScript frameworks and tools that we were talking about earlier? Well I'm going to take you through a few different examples. The first one we're going to look at is
inbox functionality. So how do you mark something as read or archived or something like that? And in this picture you'll see I've started with a basic bootstrap template just to make things look pretty real quick. And you'll see we have two messages here. The first message has been read, its background is kind of grayed out. And the bottom message there is a new message, and you'll see on the right hand side there's a button to archive it or mark it as red. Pretty straightforward. This is a really common functionality that you'll need in a number of applications And if we look at the views here, on the left is kind of just the standard approach, and on the right is the approach where we are making use of HTMX.
And you'll notice really the only difference between these two views is that if we've marked a message as red, instead of returning the entire template back to the user, We're including just a fragment. And in this case, I've put the fragment in its own directory fragments under the messaging apps templates. So we return that with a template response. Very simple. So basically if if we get a Git request, we send the entire template back. If we get a POST request. We only send that fragment. And here's the differences between the templates themselves. On the top is the traditional approach, and you'll see
You know, this is this is probably something that most of you have all seen before. Pretty straightforward. Just a form. We've got our CSRF token in there and a button that submits the form. Now the thing is that will refresh the entire page. On the bottom, very similar. We've wrapped the whole thing in a div. And I've I've kind of hidden the form and some of the other content there, but we wrap the whole thing in a div. And then you'll see instead of a button, I've got a span there just to make things easy And there's a few different HTML parameters that are different than what you would normally see. The HXPost, HXSwap, HXTarget, and HX
include Now I've prepended those with data just uh to keep my IDE from screaming because anything starting with data uh is is generally uh acceptable anything starting with something like HX is is gonna be called out even though it's uh gonna work just fine So in this example, when we click on the span that includes that little icon, the little envelope icon, what's happening here is HTMX is posting to the messaging inbox HTMX view. It's going to swap the outer HTML, which is that div
And you can see the target. We have a different div n ID for each message And then we are going to also include the contents of the hidden input there named item And the value of that hidden input is the message ID. So pretty straightforward. When you hit the icon for archiving, it'll post the message ID. So in this example we're using the same template fragment for the initial display of each message and for the content that is swapped in. And in
the main template itself, we just have something that looks like this. For each message, we include the message fragment. Pretty straightforward. The swapped content replaces the entire original content in this example. So again, when I hit that icon, The outer HTML, the entire div, is replaced with a new div that has the updated message. So here's a quick video, just uh pretty straightforward, nothing super crazy, but uh you can see when we hit the icon The page doesn't have to refresh. It just refreshes that small portion of the page
So the next example that we'll look at is one-click settings changes. So in this example when I hit the checkbox, I want that update to be made in the database without refreshing the whole page. a template only based approach uh that's really not easily possible. Um you're gonna refresh the whole page In the initial case here, you'll see that I'm using Ajax to show you what uh you know a more realistic uh initial approach might be to enable that interaction between the front end and the back end without page refreshes. In the HTMX example, I've included a small amount of hyperscript, which we'll talk about a little bit later.
to help with animation. And here we are going to return HTML directly from the view instead of using a separate template fragment And then the last thing that's kind of different here is instead of loading the response to the div that encompasses the uh the content we're going to load that response to a different location a separate div so instead of replacing the entire outer HTML We are going to replace the contents of a particular div. This is probably pretty straightforward if you've worked with Ajax before. But it might be a little bit intimidating if you're not really versed in JavaScript.
So in this example, we have a standard form with a checkbox. uh which would do be duplicated uh in in real life with multiple checkboxes. And when the checkbox is checked or unchecked we pass the value of setting one or setting two or whichever one we've just checked to the user settings view for processing on the back end. In the HTMX version, we can skip any of that uh special JavaScript. We don't need any any Ajax or or anything like that And we can just add a few bits to the HTML for the checkbox itself. Here we're posting the value. of a hidden input named settings
to the user settings HTMX view when we get a click event on the checkbox. The HTML that we get back from the view will be put into the setting one response div down there at the bottom So what do the views look like? In the traditional approach, if the request is a post , we just toggle the setting. This is pretty straightforward here. You know, we've received a post, we get the settings value from that post from the request, and then we either set or unset uh the Boolean. Now in this example in the model on the user model I've set a
model method toggle setting1 And all that does is if it's a true it sets false, and if it's a false, it sets true. And then saves to the database. In the HTMX version, we really do the same thing, but we're also going to return a chunk of HTML that displays a success alert. and uses just a little bit of hyperscript to remove that alert after two seconds. Here you can see the end result We've got interactivity with those checkboxes with just a few changes to our HTML and a few updates to the view itself to return some HTML.
This same method can really be used for any other type of instantly updating settings. So if you have uh say a select box. where the user can choose preferences for receiving emails or SMS or something like that. Or if you have text inputs or any other inputs in a form. you can use the same technique and expand it for that. And the last example that we're going to look at here is a case where you want to display multiple independent forms on the same page. Now sometimes it's easiest to put those in individual tabs or or in some other way where most of the content is hidden away until you click on something
and it changes to the content for for that thing. So in this example I've got five different forms here, but only one of them is visible to the user at any time. Each form is designed to submit separately from the others. This is really a pattern that I use a lot in my own projects. Here's an example from one of my projects where I have three different forms available to the end user for specifying the water order requests at an irrigation company. And that's not important here, but uh just so you can see, each form is is somewhat different, uh, but we kind of hide the other two forms behind tabs and it makes it easy for the irrigation company
to see all of the order form options without it cluttering up the whole page. You might use this sort of thing anywhere where you need lots of profile settings, tenant configurations, or variations on order forms like this one. To demonstrate here, I'm using just a really simple form duplicated in each tab. All we're doing is looking at the username and email for the current user In reality, you'd probably have a bunch of different forms and they might have you know dozens of different fields on each of them. In the traditional approach to the template, we load each form into each tab on the page.
So all of these forms are in the DOM content for the page itself. When you click on one of those, it just unhides the current tab and hides the one that was visible before. Or as you can see here, it literally is setting the active class on the active tag tab In the view, we have to check to see which form was intended for processing by looking at the name of the submit button associated with that form. Some of the issues with this are you have to prefix each of your forms when you're putting them all on the same page in into the same template.
Otherwise, you're going to end up with forms that have the same ID, and that's really a no-no for HTML and will cause problems. For multiple forms, the views can get really long and complex because you have to display each form on the Git request and process each form on the POST request. all in the same view, it just gets really complicated. One other potential issue is the amount of content that's loaded to a view like this. If you have select boxes that have a bunch of options in each of the forms, The page may take a long time to load because you're loading up the options for each of those select boxes at the load
of that view. So maybe some some things that you want to avoid. So I've just kind of highlighted here the points that I made before about having to prefix each form so that it's uniquely identifiable in the browser. It doesn't look so bad here with only two tabs shown, but you can imagine it really gets quickly overwhelming as the content that you're pushing into that view increases uh it it definitely gets gets long. So how would we approach this with HTMX? Well with HTMX we can split things up a bit In this case, we're going to use separate templates and views for each tab.
And that might seem like a lot more work. But I will tell you it's incredibly easier to troubleshoot and to read through and understand which bits of code do what when things are split out a little bit. We use a single div for the content of all the tabs, and we only load the content for the tab that is currently selected. When the page initially loads up, we will insert the contents for that first tab, and then when any other tab is selected, we load up the contents for that tab. Here's the update with the HTMX for the template itself. And if you look on the left, we have the base template for the view.
And one of the tab fragments is on the right. So what's on the right there is what will be loaded into the div at the very bottom when any tab is selected. Each tab has some HTMX attributes that do a Git request for the contents when that tab is clicked. And those contents, like I said, get loaded into the div at the bottom At the top there you'll see that we have another div. And what we're doing there is If you look at the HX trigger attributes of that div, it says load after 100 milliseconds. So we are loading the contents of Form
1 into the tab content div after the page has been loaded for 100 milliseconds. At the end of the day, the important thing is that each form inside the div , in the fragment there on the right, performs a post of that form to the view associated with the tab. So there's no more need for prefixing the form itself. And if we look at the views themselves. This top one, tabs HTMX view, loads up the main content. Really all it's doing is loading up a template, that base template that we looked at before. There's almost nothing to it. And then for each tab that we want, we have a view that displays the form when we do a Git
request and processes it individually when we do a POST request. Here's a video real quick just showing what it looks like. It's a very simple example here, but you could expand it considerably, and it can definitely be beneficial Keep in mind that the three examples that I've gone through here really only scratch the surface of what can be quickly implemented with HTMX. uh without the need for extra, you know, heavy frameworks and learning new programming languages. So having looked at a few examples of actually using HTMX, let's close out with some tips, some best practices
and resources that uh will help you move on with HTMX and and get involved with the community. So cross-site forgery uh request forgeries, CSRF It's an attack that forces an end user to execute unwanted actions on a web application in which they're currently authenticated. Basically, uh It it can pose a problem when you're doing posts uh posting a request, putting a request, uh submitting forms, things like that. uh it it it's a a potential issue. And Django has uh some built-in support to help you avoid CSRF attacks. It
provides a token from the back end that you can then put in the template and it compares the token that it provided with the one that is submitted when you post uh or put or or uh delete from the front end So how do we include CSRF tokens with our requests when HTMX uh isn't always wrapped in a form? We might be posting things when you click a button or something like that that doesn't have a form associated with it. Well there's two ways we can do that. We can either include the HX headers which will apply that CSRF token
header. in the post itself or you can just add this little snippet to the base template for your entire site And it will add that header on every request and just simplify things considerably. You will no longer have to think about Making sure that you have the CSRF token uh in each of your uh requests One thing that you'll notice with HTMX, and I kinda hit on it before, especially in the tab example above uh was that you may end up with more views and this is not necessarily a a negative thing uh
being able to split up the functionality into multiple views that do just small things each can really help clarify what's going on in your application. So don't be afraid of making more views. At the end of the day, it it will help, I assure you. Now, as I mentioned at the very beginning, HTMX is very focused. Its intent is not to do everything that View or Angular or React uh can do. It's its focus is really on um interactivity and uh that's that's the majority of it
So if you find yourself trying to do things like transitions and animations and things that are a bit beyond the scope of HTMX itself. There are a couple of uh recommended complementary JavaScript libraries that can help out. So first is Hyperscript. It was designed by the creator of HTMX to be complementary to HTMX. It's got its own website with some really good examples But keep in mind that its own designer will tell you that it is a pretty speculative library and subject to changes. Another option is Alpine JS, which is very lightweight and focused.
It only has a few different features, but. Just like with HTMX, you can get an incredible amount out of those few features. So both of those are worthwhile to check out. Both of them are lightweight in the sense of they aren't designed to do everything and they aren't huge code-wise, so your pages won't be drugged down in load time. Another thing to check out is Django HTMX, developed by Adam Johnson. Among other things, it provides a debug handler when the debug in your project is set to true. It'll help you with debugging things. It has a method for easily determining whether a request was made from HTMX or if it's just a standard full page request.
And then it has a bunch of other useful utilities that you'll probably find helpful as you're developing with Django and HTMX. Conveniently, Adam also includes a nice example app that demonstrates the various tools available in his package. And last, a few additional resources to check out. So htmx. org is the source for uh references for documentation for examples and for finding other resources. Awesome HTMX is a GitHub repository that lists out different blog posts, videos, and examples for HTMX. There is the HTMX Discord, which has a fantastic community.
The designer of HTMX is on there on a regular basis along with other people interested in HTMX, and there's even a Django and HTMX Discord board there. The HTMX subreddit is worth checking out. And then I've also got the notes and code for this presentation and the examples that I presented here available on GitHub. Anyway, that's it. I appreciate you coming to my talk, and hopefully it was helpful in showing what sort of things you can do with HTMX with relatively little effort. and without having to learn an entirely new language like JavaScript or or get into the guts of it.
I appreciate you coming and I hope you enjoyed the rest of the conference. Thank you. Goodbye.
HTMX extends ordinary HTML with focused interactivity such as Ajax requests, CSS transitions, WebSockets, and server-sent events. It works with Django without requiring a large JavaScript framework or a new programming language, and its compressed library is about 11 KB.
Discussed at 6:40Return the full template for a normal GET request, but return only an HTML fragment for an HTMX POST. HTMX can then target the relevant element and replace its outer HTML, so only that portion of the page updates.
Discussed at 9:06Add HTMX attributes to the input so its value is posted to a Django view when it changes, then return a small HTML response such as a success alert into a separate target element. The same pattern works for select boxes, text inputs, and other instantly saved settings.
Discussed at 14:42Use separate templates and views for each tab, with one shared content div. Load only the selected form into that div and let each form post to its own view, avoiding duplicated forms in the initial DOM, unique-prefix requirements, and a large combined view.
Discussed at 20:59You can add the CSRF token through HTMX request headers, or put a small configuration snippet in the base template so the token header is added to every HTMX request. This covers button-triggered and other non-form POST, PUT, or DELETE requests.
Discussed at 25:38The speaker recommends Hyperscript for complementary behavior such as transitions and animations, and Alpine.js as another lightweight option. Both are intentionally narrower and smaller than full front-end frameworks.
Discussed at 27:55Django HTMX provides utilities including a debug handler, an easy way to tell whether a request came from HTMX, and other development helpers. It also includes an example app demonstrating the package's tools.
Discussed at 28:40Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 14, 2026