Nikola Margit

moon indicating dark mode
sun indicating light mode

A Simple Mithril.js Counter App

January 25, 2020

In this tutorial, we will build a very simple Mithril.js counter app that will help demonstrate basic Mithril concepts, such as component and state. Instead of using create-mithril-app, as we did in the previous article, we will start from scratch. This process is well covered in Mithrill installation documentation, but nevertheless, we will lay out all the steps here.

Setting Up a Project

Create a new folder mithril-counter, and open it in terminal. Run npm init -y, a command that will generate an empty npm project without going through an interactive process.

Next step is to install the necessary tools. In this case, we only need Mithril and webpack:

$ npm install mithril --save
$ npm install webpack webpack-cli --save-dev

Last thing we need to do to finish the setup part is to add start script to package.json file:

{
// ...
"scripts": {
/...
"start": "webpack src/index.js --output bin/app.js -d --watch"
}
}

Building the App

In our root folder we need index.html file as a mounting point for our app.

<html>
<head>
<title>Mithril Counter</title>
</head>
<body>
<script src="bin/app.js"></script>
</body>
</html>

The source of the script tag is the path to a code bundle, created by webpack when we run start script. Next, we need to add two JavaScript files. Create the /src folder, and make index.js and Counter.js files inside. Our index.js will be a simple three-line file:

import m from "mithril"
import Counter from "./Counter"
m.mount(document.body, Counter)

We are using Mithril’s mount method, which connects the component to Mithril’s autoredraw system. We will also need a Counter.js file for our app.

import m from "mithril"
function Counter() {
return {
view: () => "Hello from Mithril App!",
}
}
export default Counter

What’s happening here? We created and exported Counter function, which represents a closure component, a function that returns a POJO, Plain Old Javascript Object, or, for now, just a string. The importance of using a closure component will be clearer when we start talking about handling state in Mithril components. Run npm start inside of a terminal and open index.html inside of your favourite browser and you should see Hello from Mithril App!. In order to keep this project as simple as possible, we won’t implement a live reload. You should refresh the page whenever you make a change to the code.

Handling the State Inside of Mithril Component

The cleanest way of handling the state inside of a Mithril component is via the closure component. The state of the component is simply a set of variables declared within the scope of the outer function. We change that state by using the functions we declared in the outer scope. It may sound complicated, but it will be clearer when you see it in action.

function Counter() {
var count = 0
function increment() {
count++
}
function decrement() {
count--
}
function reset() {
count = 0
}
return {
view: () => "Hello from Mithril App!",
}
}

We introduced state, count, and three functions for manipulating state. All three functions are mutating state directly. In frameworks like React, you use a built-in method, setState, which is used to change the state and to tell React that component and its children need to be updated with the new state.

Mithril uses a different approach. Changing the state won’t trigger re-render or update the DOM. Instead, re-renders will happen in three cases:

  • when event handlers fire
  • when HTTP requests made by m.request complete
  • when the browser navigates to a different route

Let’s change the view of our app:

return {
view: () => [
m("span", ".counter", count),
m("button", { onclick: increment }, "+"),
m("button", { onclick: decrement }, "-"),
m("button", { onclick: reset }, "Reset"),
],
}

Mithril uses m() utility with hyperscript to represent an HTML element. It looks like this: m(selector, attributes, children).

m("div.blue-text", { style: { color: "blue" } }, "This will render blue")

will be rendered as

<div class="blue-text" style="color: blue">This will render blue</div>

We created four HTML elements, a span for displaying current count value and three buttons. Notice that since all are the same-level elements, we returned them inside of an array. If we omitted the array, the function would only return the last element. In this case that would be a Reset button. We also connected our functions to onclick events.

Final code

Here is the final look of the Counter.js file:

import m from "mithril"
function Counter() {
var count = 0
function increment() {
count++
}
function decrement() {
count--
}
function reset() {
count = 0
}
return {
view: () => [
m(".counter", count),
m("button", { onclick: increment }, "+"),
m("button", { onclick: decrement }, "-"),
m("button", { onclick: reset }, "Reset"),
],
}
}
export default Counter

Even by building the app as simple as counter, we covered a lot of ground. We created the Mithril project from scratch and learned more about state management principles. We briefly explored the differences in approaches to state management between the React and Mithrill and covered the basics of Hypertext. With these fundamentals under our belt, we can start building more complex apps with Mithril.js.

Check out the entire code with some CSS on https://github.com/nikmargit/mithril-counter