My thoughts on Mithril.js

My thoughts on Mithril.js

Mitril.js is a client-side JavaScript framework (similar to React, Vue, Svelte, etc.)

My current go-to client-side framework is Svelte - which is amazing. The only real downside is that it cannot be used without build-tools.

So when I noticed that Mithril.js can be used without any build-tools (plain vanilla JavaScript) AND it has a tiny runtime (10 kB) AND it has real components - I was intrigued.

Having (real) components, sets it apart from other no-build / small runtime frameworks (like Alpine.js and Vue-Petite), because this allows for the creation of advanced full-size SPAs.

Basically, Mithril.js covers both scenarios - "progressive enhancement" (like Alpine.js and Vue-Petite) and full-size SPA (like React, Vue, Svelte).

Could this be the one? The single tool for everything? The holy grail?

So I played around with Mithil.js over the weekend, to see what it was like.

With Mithril.js, you generate HTML using a hyperscript dialect like this:

m("div", {style:"color:red"},
  m("a", {href:"/page2"}, "click here"))

which generates

<div style="color:red">
  <a href="/page2">click here</a>
</div>

Alternatively, you can use JSX syntax (like with React), but then you need build-tools.

Another option is using the htm library to generate the hyperscript - which would allow the use of JavaScript template literals for a development style closer to JSX or the HTML templates used with Vue / Svelte. The problem with this is that the templates would be re-compiled (by htm) on every single interaction with the app - which I imagine would seriously slow things down.

So the way I see it, the optimal way to use Mithril.js (without build tools) is to write the hyperscript style code by hand - which actually isn't too difficult once you get used to it. The biggest problem is keeping track of closing parentheses/brackets with deeply nested elements.

Reactivity / UI updates

Unlike the major frameworks (Reach, Vue, Svelte, etc.) Mithril.js does not update the UI in response to changes in application state / variables.

Instead, it updates the UI after each UI event has been handled (or whenever you tell it to by calling m.redraw()).

On each update it re-renders everything to a new virtual DOM, then diffs this to the previous version, and finally updates the real DOM with the differences.

This works surprisingly well. Dynamic and automatic UI updates - without any kind of state tracking. Very nice!

Test project

I recently created a small SPA here: https://simpledns.plus/dmarc-wizard - an in-browser tool to generate DMARC-records - an e-mail security thing.

So to try out Mithril.js, I re-created the same SPA using Mithril.js.

The following is based on my experiences doing that.

Things that I really like

  • Ability to cover both "progressive enhancement" and full-size SPA scenarios (see above).

  • That no state management system is needed / included (see above).

  • Everything, even HTML, is expressed in plain JavaScript. No "magic". This makes debugging in the browser much easier, makes code refactoring easier, and reduces the need for VSCode extensions etc.

  • The possibility of using tiny render functions, for things that would require a separate a component (and file) in Svelte. This encourages and makes it a lot easier to be adhere to the DRY principle (don't repeat yourself).

Things that really should be removed

  • Mihtril.js (v. 2.0.4) includes a wrapper for ajax calls based on XHR. They even make a selling point out of this in the very first sentence on the web-site.
    This is 2022 - all browsers now have fetch, and support async/await making fetch easy to use. Promoting XHR as a feature makes it sound rather outdated. And I would rather have an even leaner base framework.

  • Mithril.js also includes a routing feature based on "hash-bang URLs" - also a selling point in the first sentence on the web-site.
    This is of course nice to have - but routing is not needed for progressive enhancements scenarios (where I see Mithril.js having potential), and developers might prefer other routing schemes (like non-hash history-push/pop). So please remove this / make it optional (making the base framework even leaner).

  • Mithril.js includes a polyfill for Promise.
    This is 2022 - all browsers now support promise. So please remove this / make it optional (making the base framework even leaner).

Conclusion

Mithril.js has some really nice and unique features.

But, while the hyperscript syntax is doable for small widgets, I wouldn't want to use it in larger projects.

I just don't think that the hyperscript syntax is as readable as for example Vue / Svelte templates (standard HTML with as few additions).

And keeping track of those closing parentheses/brackets with deeply nested elements, just drove me nuts.

As the JSX docs put it (about something else, but with the same issue):

Unfortunately, the balanced braces do not give great syntactic hints for where an element starts and ends in large trees. Balanced named tags is a critical syntactic feature of the XML-style notation.

So to use Mithril.js, I would probably need JSX, which requires build tools. And if I have to use build tools anyway, then I still prefer Svelte...