Mithril.js
Mithril.js is a modern, small, and fast client-side JavaScript framework primarily designed for building Single Page Applications. Currently at stable version 2.3.8, it maintains an active release cadence with regular patch updates and community-driven maintenance. Its key differentiators include a remarkably small bundle size (around 8.8 KiB gzipped), a highly performant virtual DOM diffing system, and built-in functionalities for routing and XHR requests, reducing the need for external libraries. Unlike some larger frameworks, Mithril.js focuses on providing core UI primitives without dictating an entire ecosystem, allowing developers more flexibility. It supports modern browsers (IE11, Firefox ESR, and the last two versions of Firefox, Edge, Safari, and Chrome) without requiring polyfills and can be used with or without build tools, although bundlers are recommended for production environments.
Common errors
-
TypeError: Cannot read properties of undefined (reading 'someProperty')
cause Attempting to access a property on an `undefined` or `null` object within a view function, often due to asynchronous data loading not yet completing or typos in data paths.fixImplement defensive checks (e.g., `data && data.someProperty`), use optional chaining (`data?.someProperty`), or ensure data is available before rendering the affected part of the view. -
ReferenceError: m is not defined (or similar import errors like 'm is not a function')
cause Incorrect module import syntax (e.g., using `require` in an ESM file, or incorrect named/default import) or trying to use Mithril before it's loaded.fixEnsure you are using the correct import syntax for your module environment (ESM: `import m from 'mithril'`, CJS: `const m = require('mithril')`). Verify that your build tools are correctly transpiling or bundling modules. -
Uncaught (in promise) TypeError: Failed to fetch
cause This error typically originates from `m.request` due to network issues, incorrect URL, or server-side CORS policies blocking the request. It can also appear if the promise rejection from `m.request` is not caught.fixCheck your network connection and the request URL. Verify server-side CORS headers are correctly configured (`Access-Control-Allow-Origin`, etc.). Ensure you have a `.catch()` block on your `m.request` promises to handle network and HTTP errors.
Warnings
- breaking Direct assignment to `vnode.state` is no longer supported. In Mithril.js v2.x and later, `vnode.state` is an object, and properties should be assigned to it (e.g., `vnode.state.propertyName = value`) rather than reassigning `vnode.state` itself.
- breaking The `m.withAttr` utility function has been removed in Mithril.js v2.x. This function was previously used for convenient event handler parameter extraction.
- breaking The behavior of `m.request` for error handling has changed significantly in v2.x. If an `extract` callback is provided, `m.request` will no longer automatically reject the Promise for non-2xx/304 HTTP status codes (e.g., 4xx or 5xx server errors). Additionally, default `Content-Type` for JSON bodies might trigger unexpected CORS preflights.
- breaking `m.redraw()` is always asynchronous in Mithril.js v2.x. The synchronous redraw behavior that was available in earlier versions (e.g., passing a truthy value to `m.redraw()`) has been removed.
- gotcha Mithril.js's auto-redraw system does not trigger for external asynchronous events such as `setTimeout`, `setInterval`, raw Promises (not managed by `m.request`), or event handlers from third-party libraries (e.g., WebSockets).
- security A prototype pollution vulnerability was identified and fixed in `m.parseQueryString`. Attackers could potentially manipulate object prototypes via crafted query strings.
Install
-
npm install mithril -
yarn add mithril -
pnpm add mithril
Imports
- m
const m = require('mithril')import m from 'mithril'
- mount
import { mount } from 'mithril'import m from 'mithril'; m.mount(document.body, Component)
- route
const route = require('mithril').routeimport m from 'mithril'; m.route(document.body, '/', routes)
- Component Type
import m, { Vnode } from 'mithril'; function MyComponent(vnode: Vnode) { /* ... */ }
Quickstart
import m from 'mithril';
interface MyComponentAttrs {
name: string;
}
const MyComponent = {
count: 0, // Component local state
oninit: function(vnode: m.Vnode<MyComponentAttrs>) {
// Initialize state, cannot assign directly to vnode.state in v2+
vnode.state.count = 0;
},
view: function(vnode: m.Vnode<MyComponentAttrs>) {
return m('div', [
m('h1', `Hello, ${vnode.attrs.name}! Current count: ${vnode.state.count}`),
m('button', {
onclick: () => {
vnode.state.count++;
}
}, 'Increment'),
m('p', 'Click the button to increment the counter.')
]);
}
};
m.mount(document.body, { view: () => m(MyComponent, { name: 'Mithril' }) });
// To demonstrate routing (requires an element to mount routes to, e.g., <div id="app"></div>)
// const homeRoute = { view: () => m('h2', 'Welcome Home!') };
// const aboutRoute = { view: () => m('h2', 'About Us') };
// m.route(document.getElementById('app') || document.body, '/home', {
// '/home': homeRoute,
// '/about': aboutRoute
// });