WARNING: This documentation is for an old version of mithril! Please see the current docs for more accurate info.

Getting Started

What is Mithril?

Mithril is a client-side Javascript MVC framework, i.e. it's a tool to make application code divided into a data layer (called "Model"), a UI layer (called View), and a glue layer (called Controller)

Mithril is around 3kb gzipped thanks to its small, focused, API. It provides a templating engine with a virtual DOM diff implementation for performant rendering, utilities for high-level modelling via functional composition, as well as support for routing and componentization.

The goal of the framework is to make application code discoverable, readable and maintainable, and hopefully help you become an even better developer.

Unlike some frameworks, Mithril tries very hard to avoid locking you into a web of dependencies: you can use as little of the framework as you need.

However, using its entire toolset idiomatically can bring lots of benefits: learning to use functional programming in real world scenarios and solidifying good coding practices for OOP and MVC are just some of them.


A Simple Application

Getting started is surprisingly boilerplate-free:

<!doctype html>
<script src="mithril.js"></script>
<script>
//app goes here
</script>

Yes, this is valid HTML 5! According to the specs, the <html>, <head> and <body> tags can be omitted, but their respective DOM elements will still be there implicitly when a browser renders that markup.


Model

In Mithril, typically an application lives in an namespace and contains modules. Modules are merely structures that represent a viewable "page" or component.

For simplicity, our application will have only one module, and we're going to use it as the namespace for our application:

<script>
//this application only has one module: todo
var todo = {};
</script>

This object will namespace our two Model classes:

var todo = {};

//for simplicity, we use this module to namespace the model classes

//the Todo class has two properties
todo.Todo = function(data) {
    this.description = m.prop(data.description);
    this.done = m.prop(false);
};

//the TodoList class is a list of Todo's
todo.TodoList = Array;

m.prop is simply a factory for a getter-setter function. Getter-setters work like this:

//define a getter-setter with initial value `John`
var name = m.prop("John");

//read the value
var a = name(); //a == "John"

//set the value to `Mary`
name("Mary"); //Mary

//read the value
var b = name(); //b == "Mary"

Note that the Todo and TodoList classes we defined above are plain vanilla Javascript constructors. They can be initialized and used like this:

var myTask = new todo.Todo({description: "Write code"});

//read the description
myTask.description(); //Write code

//is it done?
var isDone = myTask.done(); //isDone == false

//mark as done
myTask.done(true); //true

//now it's done
isDone = myTask.done(); //isDone == true

The TodoList class is simply an alias of the native Array class.

var list = new todo.TodoList();
list.length; //0

Controller

Our next step is to write a controller that will use our model classes.

//the controller uses 3 model-level entities, of which one is a custom defined class:
//`Todo` is the central class in this application
//`list` is merely a generic array, with standard array methods
//`description` is a temporary storage box that holds a string
//
//the `add` method simply adds a new todo to the list
todo.controller = function() {
    this.list = new todo.TodoList();
    this.description = m.prop("");

    this.add = function(description) {
        if (description()) {
            this.list.push(new todo.Todo({description: description()}));
            this.description("");
        }
    };
}

The code above should hopefully be self-explanatory. You can use the controller like this:

var ctrl = new todo.controller();

ctrl.description(); //[empty string]

//try adding a to-do
ctrl.add(ctrl.description);
ctrl.list.length; //0

//you can't add a to-do with an empty description

//add it properly
ctrl.description("Write code");
ctrl.add(ctrl.description);
ctrl.list.length; //1

View

The next step is to write a view so users can interact with the application

todo.view = function(ctrl) {
    return m("html", [
        m("body", [
            m("input"),
            m("button", "Add"),
            m("table", [
                m("tr", [
                    m("td", [
                        m("input[type=checkbox]")
                    ]),
                    m("td", "task description"),
                ])
            ])
        ])
    ]);
};

The utility method m() creates virtual DOM elements. As you can see, you can use CSS selectors to specify attributes. You can also use the . syntax to add CSS classes and the # to add an id.

The view can be rendered using the m.render method:

//assuming the `ctrl` variable from earlier
m.render(document, todo.view(ctrl));

Notice that we pass a root DOM element to attach our template to, as well as the template itself.

This renders the following markup:

<html>
    <body>
        <input />
        <button>Add</button>
        <table>
            <tr>
                <td><input type="checkbox" /></td>
                <td>task description</td>
            </tr>
        </table>
    </body>
</html>

Data Bindings

Let's implement a data binding on the text input. Data bindings connect a DOM element to a javascript variable so that updating one updates the other.

m("input")

//becomes
m("input", {value: ctrl.description()})

This binds the description getter-setter to the text input. Updating the value of the description updates the input when Mithril redraws.

var ctrl = new todo.controller();
ctrl.description(); // empty string
m.render(todo.view(ctrl)); // input is empty
ctrl.description("Write code"); //set the description in the controller
m.render(todo.view(ctrl)); // input now says "Write code"

Note that calling the todo.view method multiple times does not re-render the entire template.

Mithril internally keeps a virtual representation of the DOM in cache, scans for changes, and then only modifies the minimum required to apply the change.

In this case, Mithril only touches the value attribute of the input.


Bindings can also be bi-directional: that is, they can be made such that, in addition to what we saw just now, a user typing on the input updates the description getter-setter.

Here's the idiomatic way of implementing the view-to-controller part of the binding:

m("input", {onchange: m.withAttr("value", ctrl.description), value: ctrl.description()})

The code bound to the onchange can be read like this: "with the attribute value, set ctrl.description".

Note that Mithril does not prescribe how the binding updates: you can bind it to onchange, onkeypress, oninput, onblur or any other event that you prefer.

You can also specify what attribute to bind. This means that just as you are able to bind the value attribute in an <select>, you are also able to bind the selectedIndex property, if needed for whatever reason.

The m.withAttr utility is a functional programming tool provided by Mithril to minimize the need for ugly anonymous functions in the view.

The m.withAttr("value", ctrl.description) call above returns a function that is the rough equivalent of this code:

onchange: function(e) {
    ctrl.description(e.target["value"]);
}

The difference, aside from the cosmetic avoidance of anonymous functions, is that the m.withAttr idiom also takes care of catching the correct even target and selecting the appropriate source of the data - i.e. whether it should come from a javascript property or from DOMElement::getAttribute()


In addition to bi-directional data binding, we can also bind parameterized functions to events:

m("button", {onclick: ctrl.add.bind(ctrl, ctrl.description)}, "Add")

In the code above, we are simply using the native Javascript Function::bind method. This creates a new function with the parameter already set. In functional programming, this is called currying.

The ctrl.add.bind(ctrl, ctrl.description) expression above returns a function that is equivalent to this code:

onclick: function(e) {
    ctrl.add(ctrl.description)
}

Note that when we construct the parameterized binding, we are passing the description getter-setter by reference, and not its value. We only evaluate the getter-setter to get its value in the controller method. This is a form of lazy evaluation: it allows us to say "use this value later, when the event handler gets called".

Hopefully by now, you're starting to see why Mithril encourages the usage of m.prop: Because Mithril getter-setters are functions, they naturally compose well with functional programming tools, and allow for some very powerful idioms. In this case, we're using them in a way that resembles C pointers.

Mithril uses them in other interesting ways elsewhere.


To implement flow control in Mithril views, we simply use Javascript:

//here's the view
m("table", [
    ctrl.list.map(function(task, index) {
        return m("tr", [
            m("td", [
                m("input[type=checkbox]")
            ]),
            m("td", task.description()),
        ])
    })
])

In the code above, ctrl.list is an Array, and map is one of its native functional methods. It allows us to iterate over the list and merge transformed versions of the list items into an output array.

As you can see, we return a partial template with two <td>'s. The second one has a data binding to the description getter-setter of the Todo class instance.

You're probably starting to notice that Javascript has strong support for functional programming and that it allows us to naturally do things that can be clunky in other frameworks (e.g. looping inside a <dl>/<dt>/<dd> construct).


The rest of the code can be implemented using idioms we already covered. The complete view looks like this:

todo.view = function(ctrl) {
    return m("html", [
        m("body", [
            m("input", {onchange: m.withAttr("value", ctrl.description), value: ctrl.description()}),
            m("button", {onclick: ctrl.add.bind(ctrl, ctrl.description)}, "Add"),
            m("table", [
                ctrl.list.map(function(task, index) {
                    return m("tr", [
                        m("td", [
                            m("input[type=checkbox]", {onclick: m.withAttr("checked", task.done), checked: task.done()})
                        ]),
                        m("td", {style: {textDecoration: task.done() ? "line-through" : "none"}}, task.description()),
                    ])
                })
            ])
        ])
    ]);
};

Here are the highlights of the template above:

  • The template is rendered as a child of the implicit <html> element of the document
  • The text input saves its value to the ctrl.description getter-setter we defined earlier
  • The button calls the ctrl.add method when clicked. The .bind(ctrl, ctrl.description) idiom is a functional curry.

    In this example, it's only used to maintain the scope binding for the this parameter in the controller method, but typically it's also used to bind parameters to the function without the need to declare a wrapper anonymous function.

  • The table lists all the existing to-dos, if any.
  • The checkboxes save their value to the task.done getter setter
  • The description gets crossed out via CSS if the task is marked as done
  • When updates happen, the template is not wholly re-rendered - only the changes are applied

When running the classes in this application separately, you have full control and full responsibility for determining when to redraw the view.

However, Mithril does provide another utility to make this task automatic.

In order to enable Mithril's auto-redrawing system, we run the code as a Mithril module:

m.module(document, todo);

Mithril's auto-redrawing system keeps track of controller stability, and only redraws the view once it detects that the controller has finished running all of its code, including asynchronous ajax payloads.

Also note that this mechanism itself is not asynchronous if it doesn't need to be: Mithril does not need to wait for the next browser repaint frame to redraw - it doesn't even need to wait for the document ready event on the first redraw - it will redraw immediately upon script completion, if able to.


Summary

Here's the application code in its entirety:

<!doctype html>
<script src="mithril.js"></script>
<script>
//this application only has one module: todo
var todo = {};

//for simplicity, we use this module to namespace the model classes

//the Todo class has two properties
todo.Todo = function(data) {
    this.description = m.prop(data.description);
    this.done = m.prop(false);
};

//the TodoList class is a list of Todo's
todo.TodoList = Array;

//the controller uses 3 model-level entities, of which one is a custom defined class:
//`Todo` is the central class in this application
//`list` is merely a generic array, with standard array methods
//`description` is a temporary storage box that holds a string
//
//the `add` method simply adds a new todo to the list
todo.controller = function() {
    this.list = new todo.TodoList();
    this.description = m.prop("");

    this.add = function(description) {
        if (description()) {
            this.list.push(new todo.Todo({description: description()}));
            this.description("");
        }
    };
};

//here's the view
todo.view = function(ctrl) {
    return m("html", [
        m("body", [
            m("input", {onchange: m.withAttr("value", ctrl.description), value: ctrl.description()}),
            m("button", {onclick: ctrl.add.bind(ctrl, ctrl.description)}, "Add"),
            m("table", [
                ctrl.list.map(function(task, index) {
                    return m("tr", [
                        m("td", [
                            m("input[type=checkbox]", {onclick: m.withAttr("checked", task.done), checked: task.done()})
                        ]),
                        m("td", {style: {textDecoration: task.done() ? "line-through" : "none"}}, task.description()),
                    ])
                })
            ])
        ])
    ]);
};

//initialize the application
m.module(document, todo);
</script>

Notes on Architecture

Let's look at each MVC layer in detail to illustrate some of Mithril's design principles and philosophies:

Model

Idiomatic Mithril code is meant to apply good programming conventions and be easy to refactor.

In the application above, notice how the Todo class can easily be moved to a different module if code re-organization is required.

Todos are self-container and their data aren't tied to the DOM like in typical jQuery based code. The Todo class API is reusable and unit-test friendly, and in addition, it's a plain-vanilla Javascript class which requires almost no framework-specific learning curve.

m.prop is a simple but surprisingly versatile tool: it's composable, it enables uniform data access and allows a higher degree of decoupling when major refactoring is required.

When said refactoring is unavoidable, the developer can simply replace the m.prop call with an appropriate getter-setter implementation, instead of having to grep for API usage across the entire application.

For example, if todo descriptions needed to always be uppercased, one could simply change the description getter-setter:

this.description = m.prop(data.description)

becomes:

//private store
var description = data.description;

//public getter-setter
this.description = function(value) {
    if (arguments.length > 0) description = value.toUpperCase();
    return description;
}

According to Mithril's philosophy, list and description are also considered model-level entities. This is a subtle but important point: model entities don't need to be full-blown custom classes.

Native javascript classes are quite appropriate for storing primitive and structured data. Since in this case they are indeed being used to store data - even if temporarily - they are model entities!

Be aware that by using the native Array class for a list, we're making an implicit statement that we are going to support all of the standard Array methods as part of our API.

While this decision allows better API discoverability, the trade-off is that we're largely giving up on custom constraints and behavior. For example, if we wanted to change the application to make the list be persisted, a native Array would most certainly not be a suitable class to use.

In order to deal with that type of refactoring, one can explicitly decide to support only a subset of the Array API, and implement another class with the same interface as this subset API.

Given the code above, the replacement class would only need to implement the .push() and .map() methods. By freezing APIs and swapping implementations, the developer can completely avoid touching other layers in the application while refactoring.

todo.TodoList = Array;

becomes:

todo.TodoList = function () {
    this.push = function() { /*...*/ },
    this.map = function() { /*...*/ }
};

Hopefully these examples give you an idea of ways requirements can change over time and how Mithril's philosophy allows developers to use standard OOP techniques to refactor their codebases, rather than needing to modify large portions of the application.


Controller

Mithril follows a data binding paradigm that is familiar to developers that use server-side MVC frameworks like Rails and Django.

The difference, as mentioned earlier, is that Mithril philosophy considers any form of data storage as being a model entity - even data from a text input waiting to be saved!

In Mithril, controllers are not meant to progressively operate on model entities. Instead, model entities should expose methods that atomically act on themselves.

What this rule means is that controllers can have conditional logic, as is the case in the add method in the application above, but each action that touches a model entity should not leave it in an unstable state.

This is in contrast to the ActiveRecord pattern of other frameworks, which allows entities to be in potentially invalid states (for example, a to-do with no description), so long as they are not "saved".

The idea of disallowing unstable states hinges largely on the developer deciding what constitutes validity:

  • An empty description in the context of the text input in the UI is a perfectly valid state, and a string is an appropriate type to express that.

  • A to-do with no description is not valid, therefore we avoid writing code that ever leaves the Todo class instance in a unstable state.

Mithril doesn't programmatically define the scope of each model entity or in what states an entity is considered valid - validity is something the developer is responsible for defining.

Mithril's philosophical framework simply encourages that the developer map validity to static types. This is a key step in ensuring programs are robust and refactorable.


View

The first and most obvious thing you may have noticed in the view layer is that the view is not written in HTML.

While superficially this may seem like an odd design, this actually has a lot of benefits:

  • No flash-of-unbehaviored-content (FOUC). In fact, Mithril is able to render a fully functional application - with working event handlers - before the "DOM ready" event fires!

  • There's no need for a parse-and-compile pre-processing step to turn strings containing HTML + templating syntax into working DOM elements.

  • Mithril views can provide accurate and informative error reporting, with line numbers and meaningful stack traces.

  • You get the ability to automate linting, unit testing and minifying of the entire view layer - and you are even able to use Closure Compiler's Advanced Mode without needing extensive annotations.

  • It provides full Turing completeness: full control over evaluation eagerness/lazyness and caching in templates. You can even build components that take other components as first-class-citizen parameters!

  • Turtles all the way down: you don't need write custom data binding code in jQuery for every possible user interaction, and you don't need to support a complicated "directive" layer to be able to fit some types of components into the system.

Views in Mithril use a virtual DOM diff implementation, which sidesteps performance problems related to opaque dirty-checking and excessive browser repaint that are present in some frameworks.

Another feature - the optional m() utility - allows writing terse templates in a declarative style using CSS shorthands, similar to popular HTML preprocessors from server-side MVC frameworks.

And because Mithril views are javascript, the developer has full freedom to abstract common patterns - from bidirectional binding helpers to full blown components - using standard javascript refactoring techniques.

Mithril templates are also more collision-proof than other component systems since there's no way to pollute the HTML tag namespace by defining ad-hoc tag names.

A more intellectually interesting aspect of the framework is that event handling is encouraged to be done via functional composition (i.e. by using tools like m.withAttr, m.prop and the native .bind() method for currying).

If you've been interested in learning or using Functional Programming in the real world, Mithril provides very pragmatic opportunities to get into it.


Learn More

Mithril provides a few more facilities that are not demonstrated in this page. The following topics are good places to start a deeper dive.

Advanced Topics

Misc