Evan Boehs website Mastodon PGP Key email A drawing of an astronaut in space United States is setting sail

Progressive enhancement

in
garden

A little while ago, I announced to the world that I was looking for a compile time framework. I’ve learned lots since that writing, more then I imagine most frontend developers ever will.

I don’t blame them. It’s easy to get all caught up, to make things fast, to make things easy for one self. It’s human. This is not helped by the developer’s boss. Let’s be honest for a second. React developers don’t like react, but that’s where the money is. React developers have long given up on the art of development, instead motivated by deadlines and their horrible, horrible, profit driven boss.

Sorry, that was controversial. Can you tell I don’t like react?

Actually, for a while, I liked no frameworks. I took great issue with a precieved problem. I believed, and was lead to believe, that frameworks make websites inaccessible to those without JavaScript. That’s not unreasable, take any framework, disable JavaScript, and you should expect to be met with blinding white. Every. Single. Time. To illistrate my point, I disabled JavaScript everywhere, expecting chaos. But the web worked. Surprisingly well. Most of the web was indeed broken, but a startling amount of it did work, or at least was usable.

How can this be? I don’t think many people are doing things the good ol fashion template driven way. Frankly, I don’t think many people have actually stopped to test if their app works without JavaScript! The answer is serverside rendering. Basically, modern backend frameworks like Nuxt, Next, and whatever-is-hip-now render the page on their servers before sending it to you. This seems confusing, because if you open your developer tools you can clearly see hundreds of kilobytes of JavaScript. How does it make sense that the app is so fast if the server is rendering each frame?

Well, it’s really not. The point of serverside rendering is to make initial load times fast, not to keep the page working without javascript. This has a side effect. If an app is serverside rendered and I click a link without JavaScript, it will work, it simply does it’s duty once again and renders a single frame, unknowing of the fact that both visits were within the same session. The same goes for forms. Websites using sever side rendering with normal links and forms benefit from Progressive Enhancement. The framework will hijack those forms and links to make them fast, but they still work. That’s important.

The problem is it’s uncommon to see frameworks embrace this method of client server communication. Most of the time, developers will use convenient click events and fetch requests. Frameworks make these non-html based modes of communication feel good. That’s not great, because it breaks progressive enhancement.

When talking about frameworks for progressive enhancement, it’s important to keep in mind that almost all frameworks do support it to a degree via SSR. Instead, we are looking for frameworks that embrace progressive enhancement and standards, going out of their way to make it better.

Buzzwords

Progressive enhancement has many words. “resumable javascript”, “isomorphic javascript/forms”, and “server side rendering” are all good words to look out for.

Isomorphic

Being able to run on both the client and the server. It can be used interchangeably with “server side rendering”. It is related to progressive enhancement because a fully isomorphic framework requires no client side JavaScript, truly progressive.

Most current “isomorphic” frameworks are abusing frameworks not designed to be isomorphic. This makes designing fully isomorphic experiences in those ecosystems feel unnatural.

Resumable

Means finishing or “resuming” the work already done by the server. The server takes it most of the way, the client finishes it. To support this, SSR must be great on the server.

Frameworks

For the record, I have saved the best for last.

Web Components/Lit || Embedded forms + SSR

Web components are designed in such a way that they have become prime for basic progressive enhancement. They can easily be used to “enhance” the HTML elements we know and love. Take Remy Sharp's 2019 article on progressive enhancement. They give a perfect example of a place web components can be used. Credit card forms. The following is a HTML form that works on all browsers.

<input type="text" name="creditcard" required autocomplete="cc-number">

But it lacks real time validation and styling. We could build our own credit card library, or we could build upon it.

<stripe-cc-card>
	<input type="text" name="creditcard" required autocomplete="cc-number">
</stripe-cc-card>

<stripe-cc-card> represents a web component or other client side JavaScript. It builds upon web standards, adding interactivity via a wrapper. The important thing is that if the JavaScript fails to execute then the input is still present and working.

This requires your backend to embrace forms as it’s communication, an unconventional pattern the frameworks below might help you do.

Hotwire (Stimulus/Turbo)

This is me signing up for an account at hey.com without javascript, a website powered by hotwire

This is me doing the same with JavaScript

Do you notice something amazing? The user experiences are almost exactly the same. basecamp is using progressive enhancement to make loading fast, and is rendering forms on the server with rails. Turbo does this without any custom JavaScript. I’m not comfortable with Rails, and need a more advanced UI framework. Hotwire does not concern it’self with the framework you use.

SvelteKit

This is the first actual framework, or rather backend. Because it handles SSR well, right off the bat we get a fully functional page.

Rill.js

Remix

SolidStart

/node/progressive-enhancement.html