--- title: HTML Over The Wire (vs. JSON) permalink: /futureproof/html-over-the-wire/ description: Today, I took a deep dive into the world of FastHTML and HTMX, exploring their potential to revolutionize Python web development. I detailed how these tools enable the creation of dynamic web applications by sending HTML over the wire, eliminating the need for complex JavaScript frameworks and JSON APIs. Through a practical example, I demonstrated how to build a simple app that updates content dynamically with HTMX, showcasing the power of this approach for creating interactive user experiences with minimal code. I also discussed the mindset shift required to embrace this new paradigm, emphasizing clarity, explicitness, and the “WET” philosophy as keys to success. meta_description: Discover how FastHTML and HTMX enable dynamic web apps by sending HTML over the wire—cutting through JSON complexity with minimal JavaScript. meta_keywords: FastHTML, HTMX, HTML over the wire, Python web development, dynamic web apps, interactive UI, minimal JavaScript, server-side HTML, explicit rendering, web framework simplicity layout: post sort_order: 1 --- {% raw %} ## Starting Fresh with Good Sleep Let's see how much we can get done in one day. It's about 8:00 AM. I had planned on going to sleep by 9:00 PM last night and waking up in the middle of the night and get some of that strange out-of-time nighttime coding time in, when the rest of the world is asleep and flow-state is easily slipped into. Then I started listening to that sleep research book, Why We Sleep by Matthew Walker and wised up. It's way better to start fresh with the superpower of a good night's sleep, which ended up being about 9 hours! And that was after contemplating the problem I'd be tackling when I next sat down at the computer, a difficult bit of coding I've been building up to for a few days now. Well, it's the weekend so let's see if we can slam out the difficult bit here at the leading end and dedicate the rest of the weekend to making sure I can pull the trigger on this thing. ## The DRY vs WET Coding Dilemma Okay, deep breath... the issue is DRY vs WET coding style. I started out WET, as WET as you can get, and the burden of overarching system pattern refinement weighed me down. Every time I had a new "best practices" rule in the system, I had to go change it everywhere it was written twice (WET = write everything twice). And so the code surface area that I was allowing to be expanded out for unbridled Jupyter Notebook-like customization was bogging me down in the earlier stages of development, so I DRY'd it up. That is, I moved the repetitive code into a loop. I even went further and moved all common parts into a class that everything could be based on, called a superclass, and inherited all the workflow's properties from the superclass, expecting that customization would be selective overriding of the superclass. ### The Friction of Superclass Customization This is all very elegant and reduces the amount of code in the idealized template where nothing is customized yet. However the problem is the friction on the very first instance of customization. You have to know about the subclassing from a superclass and selectively overriding, with all the Python object oriented programming nuances. Now, I don't love OOP in Python, but neither am I any stranger to it. There's another part of Pipulate with much more traditional Rails-like CRUD operations (Create, Read, Update, Delete), which is never going to be customized off the beaten track much, and so I kept the superclass scheme. I was able to customize it between the app that manages profiles and the app that manages todo lists with no muss and fuss. But Workflows are not CRUD apps. Workflows, especially ones trying to capture Notebook cell flexibility, vary wildly. Having to have pretty intimate pre-knowledge of the superclass workflows inherit from so you can selectively override them in Python's nuanced way of doing so is the very definition of cognitive overhead. Bye by DRY! DRY is just not worth it for the sake of less code in a template and idealized examples! ### Converting Jupyter Notebooks to Pipulate I already obliterated the superclass inheritance was based on. That's done. All I have left now is a loop where there should not be one. The loop is collapsing surface area that locks the first and second instance of a web form together. And it is the Hello World example of Pipulate. The Jupyter Notebook we are converting is effectively:  ...which if you were to download it and show it as an executable script, would look like this. It's interesting to note that the Notebook cell delineation is kept intact along with the execution-count numbers that were in your notebook when you save the executable version. You get this version of your Notebook by going in JupyterLab to: `File / Save and Export Notebook As / Executable Script` ...and it will end up in your `~/Downloads` folder! This is a wonderful first step when doing a port from JupyterLab to Pipulate. ```python #!/usr/bin/env python # coding: utf-8 # In[1]: a = input("Enter Your Name:") # In[2]: print("Hello " + a) # In[ ]: ``` ## Converting Jupyter Notebooks: The Harsh Truths Fair warning. This tiny bit of code is going to expand out to what appears like an ungodly amount of code in Pipulate for what it's accomplishing. And it's time to lay out a few harsh truths. ### The Real Purpose: Learning HTMX with FastHTML This is not intended to be an easy-peasy notebook-to-webapp converter. This is to teach you HTMX under Python in the FastHTML framework. It is my way of forcing myself to ***get HTMX into my fingers!*** We are trying to achieve FastHTML/HTMX fluency, and this new approach to web development looks strange, acts strange... is strange! This is not so much a simplification as it is a transformation of coding style. ### The Path to Automaticity What today is stumbling blocks and frustration will tomorrow be automatic and natural. The word for this getting it into your fingers muscle memory thing is automaticity. We're going to Automatic City! We're gonna become the Wizard! And to imagine the City is based on [Itty](https://pypi.org/project/itty/). ### A Brief History of Web Frameworks Hmmm. Okay, poetry struck. Let's get it out of my system and give a bit of a history lesson. I tried Ruby on Rails when it came out and was all gangbusters, and I hated it. I hate it for the same reason I'm not a LangChain fan today. Frameworks that you start out with have to be unopinionated. Or perhaps more accurately, the opinions they express strongly must enhance and not conflict with the strong opinions you're about to layer in yourself with your own framework! That's what drove Flask's popularity and is poised to drive FastHTML's popularity today, because of HTMX's game-changing potential. Ahem, so the history goes: > Ruby shrank rails with [Sinatra](https://github.com/sinatra/sinatra) > Python made an [itty](https://github.com/toastdriven/itty/) bitty homage > Which got [bottle.py](https://bottlepy.org/docs/dev/)'d up in a [flask](https://flask.palletsprojects.com/en/stable/) app > Copycats [CherryPy](https://pypi.org/project/CherryPy/) picked and [Pylons](https://pypi.org/project/Pylons/) > JavaScript envy [Fast(API)](https://fastapi.tiangolo.com/) tracked it all > But its [pydantic](https://docs.pydantic.dev/) insanity can go to [FastHTML](https://www.fastht.ml/) ### The Evolution of Web Development Frameworks The insider *"just have to know"* vibe here is off the charts, I know. But in brief, Ruby on Rails, a ***joyful*** framework showed everyone how awesome web development could actually be, after a long winter of Java [hibernation](https://en.wikipedia.org/wiki/Hibernate_(framework)) in a [cocoon](https://en.wikipedia.org/wiki/Apache_Cocoon). Oops, I did it again. ### The Rise of FastAPI and Its Limitations Anyway, Ruby is so highly opinionated in what it encourages you to build, it turned off people like me, so Ruby Sinatra was made as an example of how to minimize Rails with a lovely function router, and not much more. Python implementations followed, including Itty and Bottle, but Flask is the one that stuck. Lots of copy-cats spun different versions of Flask for their interesting use-cases, the biggest of which is today's reining champion, FastAPI, due to it supporting Python 3's asynchronous mode before Flask -- something everyone was clamoring for. ### The Problem with Modern Python Web Development To put the cherry on top, FastAPI also supported (enforced) Python's new MyPy declarative variable typing (the opposite of duck typing), which combined with asynchronicity for big performance increases over Flask -- something which the Python world was clamoring for since the JavaScript side of webdev was undergoing profound performance optimization (WebAssebly aka WASM). So FastAPI scratched one very important itch, one that kept Python having validity in the enterprise world where Python could be disqualified for a project based on every clock-cycle slower than it was than JavaScript for the same app. In other words, FastAPI walked into the perfect storm of adoption, because of all the pent-up JavaScript-envy in the Python world. The rush from Flask to FastAPI made a sonic boom. ### FastAPI Scratched The Wrong Itch Well, the itch FastAPI scratched ain't my itch. I couldn't care a wit for enterprise scaling performance. I'm a lone coder trying to keep my secret-weapon project code under control. And that means within my understanding, reduced code complexity and surface area, fewer moving parts, less dependencies. You get the idea? What's worse, the pedantic Pydantic library, the thing that enforces strict static typing of variables, creates huge cognitive overhead. And it doesn't solve the big itch in Python web development for me: it sucks! Python webdev sucks because of templating languages like Jinja that force all this mixed-context nonsense, which is yet more cognitive overhead. And what's worser than worse is that FastAPI doesn't alleviate the need to use JavaScript all over the place browser-side (client-side) in order to continue looking, feeling and performing like a modern web app. So, you might as well be using JavaScript for Web Development. And since JavaScript is so antithetical to my vibe, I'd rather not be doing webdev at all than to be forced to become a ***full web stack*** developer (node, sass, react, building software, etc.) That left a vacuum in Python webdev for people like me. I have no interest in bigger, larger frameworks even if they do squeeze out some extra performance on the server. I want simplicity! ### The Game-Changing Convergence: Carson Gross and Jeremy Howard And that catches up to the present. All that's changed because the work of a guy named Carson Gross quite literally dovetailed in the most elegant and poetic sense you can imagine (HTML attributes <-zip-> Python arguments), with the work of a guy named Jeremy Howard. Carson's work is HTMX which beats the L out of HTML... with really good X? Anyway, HTMX is HTML perfected -- or at least the hypertext markup language specification a bit more fully finished. Page-reloading not required! Just any old element can update or be updated by any other element without a page reload -- including of course, by the server itself. And in the end, that makes all the difference. React, Vue, Angular, Svelte, Solid, Kwik, Preact, yadda yadda... all not necessary anymore! You might even call them obsolete. Thanks, Carson! Oh, and Jeremy? Well, Mr. Howard simply poured the new HTMX standard into a Flask-like Python web microframework... while eliminating the templating language like Jinja, which while in itself is not unique (pip install dominate and others did it before him), Jeremy is the first one to do it *WITH HTMX!* The elegance here if it's not clear is that Python expression, For web-sidious is less hideous & cures full-stack depression! ### Embracing the New Pattern There really are no words. Just do it. And get over the new-pattern convulsions. They will be ugly! Just get over it. It will be natural in a few weeks. I just need the killer starting template. ### Understanding the HTMX Style Ugliness starts with returning values from Python functions which are themselves never named objects or variables in the first place, but are actually the HTML that you're passing on the wire. A particular style develops here, where while not technically functional, you do avoid arbitrarily named variables that litter up your code for a mere one or two uses. In the spirit of Python, stripping out everything that's not absolutely necessary and thus creating visual clutter and cognitive overhead -- the reason for whitespaces and indents versus curly-braces for block delineation -- a little-used feature of Python is commonly employed with HTMX: line-breaks being allowed between parenthesis in function calls! ### Building HTML Fragments with HTMX So HTMX often calls on you to return an HTML fragment from a function. But HTML is constructed from Python functions, commonly `
` or `