Mithril vs. Angular vs. React

January 2017 note: This article is somewhat dated now. Please read http://mithril.js.org/framework-comparison.html for a more up-to-date comparison article.

The Mithril.js authors are onto something with their tiny (7KB gzipped), easy-to-learn JavaScript MVC library that was originally released in March of 2014 and now has 92 contributors on GitHub. The entire API is just 14 functions and the library is under a thousand lines of code. Compare that to Angular which has about 9,200 lines of code, 170 services/factories/directives, as well as global APIs, filters, and an Angular-specific module system. (Angular 2.0 drops the module system for ES6 modules.) The original author of Mithril worked on large Angular projects at his day job and what he learned from Angular in a large codebase influenced the design of Mithril. In fact after writing mithril.js his company switched to it.

Features. Mithril gives you hierarchical MVC components (just like React), URL routing, customizable data binding, and safe-by-default templates with intelligent DOM difference checking for high-performance rendering. React also uses a DOM difference strategy.

Unlike most other frameworks Mithril does not force you to extend framework classes, allowing you to structure your code however you prefer. Mithril is orthogonal to the module system and compiled syntaxes you use so you are free to use ES3, ES5, ES6, asynchronous module definitions (AMD), commonJS modules, browserify, webpack, TypeScript (Mithril includes a TypeScript definition), sweet.js, babel.js, etc.

In React you will extend classes, while in Mithril you create components with an optional controller and a required view property.

Documentation. Mithril has more documentation in its Github repo than source code and none of the documentation is auto-generated. The small API helps with the learning curve and there are good examples. You only have to learn one function, m.mount, to get started building an app with Mithril. Coming from Angular, the Mithril documentation is miles better, plus Leo Horie has an incredible, extensive blog, with real life scenarios.

Performance: Mithril itself loads in under 1ms, compared to 7ms for Angular, and 25ms for React. It renders in under 10ms, compared to 119ms for Angular, and 80ms for React. It is also the fastest MVC library in the TodoMVC benchmark.

This JSPerf demonstrates Mithril outperforming Angular, Ember, Knockout, and React by a couple orders of magnitude. Performance is critical with higher page complexity and for older browsers and mobile devices which tend to render web sites a lot slower than than laptops and desktops.

Compared to React, you are getting an order of magnitude faster performance with an order of magnitude less code.

Templates. Typical MVC frameworks use templates in HTML (sometimes called partials). These are good for a lot of static content because they are easy to read. However they create complexity because they effectively invent a new programming language within the template. This is partly why there are so many MVC libraries — different takes on the template language, with slightly different syntaxes for iterating and data binding. Angular’s compiler and parser for its HTML templates constitute about 4,000 of its 9,200 lines of code.

Mithril, Mercury, and React argue that separating your markup from your view logic is a separation of technology, not concerns. You can write declarative views in a functional style in JavaScript. The Mithril blog details this.

Another downside of HTML templates is they can’t be debugged. This is not just a theoretical concern. Often when there is a bug somewhere, the whole page won’t render and its very difficult to figure out why. In Angular, it will often throw an “invalid expression” exception which does not give any context about where the invalid expression is. While that code looks like JavaScript, it is really Angular expressions, which are subtly different from JavaScript. Again, an unintended consequence of having program logic in HTML.

With template logic being just JavaScript it’s easily debugged and profiled in the browser. Development is much faster when you can debug your views. The developer can also modularize all aspects of his templates, as well as use other library code in his templates in the normal JavaScript ways—things that are not possible to do with HTML-based templates. There is also no need for hacks like directive priorities.

With Angular —in a lot of ways, you get good at writing Angular. You master Angular specific concepts like directives, scopes, and services. You can see how many of the Angular directives (e.g., ng-repeat, track by, ng-repeat-start/ng-repeat-end, ng-options, ng-init, filters, $index, $scope semantics, and error handling semantics) duplicate programming language functionality. With Mithril and similar libraries — you write more JavaScript. How much of your MVC template language will live for the next 10 years? How much of your JavaScript code will live for the next 10 years?

Directive priorities are another consequence of program logic in HTML. They are an internal magic number used to resolve order when there are multiple directives defined on a single DOM element in Angular. So if you need to figure out the order of operations of multiple directive on a element, you need to open the source code for each one. With regular function calls in JavaScript views, the order of operations is of course obvious and controllable by the programmer.

As far as Angular 2.0, the template syntax has changed. You can argue about if it is better or not (it looks like a nice improvement to me), but the fact that it changed in the first place highlights the consequence of having view code in HTML.

Angular 2.0 is also using ES6 modules and TypeScript and introduces annotations. I think ES6 and TypeScript are great additions to the language, but I am a little bit wary of annotations, which seem unnecessary in JavaScript (you can already add arbitrary properties to objects) and question the need for a MVC library to require the user to extend its classes (remember mootools?).

Annotations are nice when they work which is 80% of the time, but when they don’t work or you need to understand how they work, can you set a breakpoint in them? At that point they become mysterious magic. In a lot of Java frameworks like Spring almost everything is an annotation rather than a function call, and because of this you can’t debug them or understand the program flow control. You don’t immediately know when they are processed or in what order…or even if they are processed at all. (In fact some of the annotation processing has become so complex, they’ve started adding ordering annotations.) Do we really want the same situation for JavaScript?

Like templates, annotations tend to evolve into a whole language of their own, with all the requisite complexity therein and can require another step in your build to handle. Of course with Mithril you aren’t extending framework classes so you don’t need annotations in the first place.

Testability. Since Mithril templates are just JavaScript you can test them in any JavaScript engine without a build step.

What about React? Mithril and react are very similar. I hear about many developers who know React really liking Mithril. They are both component-oriented and use a virtual DOM system. Both libraries essentially give you “web components” without web components.

Mithril I personally perhaps a little simpler with a smaller library file size, but Inferno is faster now and there are a lot more components available in the React ecosystem.

A Mithril component is just an object with a view property and an optional controller property (in React you extend a class but they do have functional components now). I like the way arguments are passed as options to components in Mithril and how those are passed as arguments to the view and controller. I also like how you can supply a “config” function on a virtual DOM node, and it will be called with the actual DOM element. Edit: React has a similar mechanism now.

Conclusion. There is a trend now towards component-style front end libraries (Inferno, Mercury, React, Mithril, etc.). I like Mithril and React because they are fast, small, and easy to use. I am much more productive in them than Angular or Backbone. It seems like there is still a lot of innovation in this space and I think we will continue to see more discovery and cross-collaboration across MVC libraries. See for instance the VDom benchmark.

Check out https://github.com/llambda/mithril-modal for a simple starter project that shows how to build a modal dialog component.

And http://lhorie.github.io/mithril-blog/an-exercise-in-awesomeness.html has a great example of occlusion culling in Mithril!

Update a few months later:

A few things after having used Mithril for a while now.

  • It gets out of your way when you need it to, and gives you a straightforward way to directly get the DOM objects when you need them; very important for animation or using 3rd party code like Google Maps.
  • The component mechanism new to 0.2.0 is simple and fantastic. It allows my code to be split up into separate modules with clearly defined interaction boundaries. Web components without needing web components.
  • The template syntax you get used to somewhat, but editors don’t necessarily do a great job re-indenting or closing parenthesis and brackets (similar problem in Scheme). You end up having to count parenthesis and brackets. HTML markup is much easier to do in MSX/JSX.
  • Update: With the “Atom Beautify” plugin for atom editor, the formatting becomes much easier. I got used to it over time, and haven’t bothered with MSX though I think I still might do MSX/JSX on future projects. I still have to count parenthesis and brackets sometimes which is quite maddening, but with the code indented well, that is not usually a problem.
  • m.request gives you a nice way to access the XHR directly. So you can do things like aborting requests and using progress events and uploading files. Though I am using fetch now these days mostly.
  • Using Redux with Mithril works great (see https://github.com/tivac/mithril-redux )