Mithril's Promise‑Driven Routing: A Practical Guide for SPA Developers
Mithril's promise‑driven routing lets you load data before a view appears, keeping SPA navigation tidy while avoiding manual loading boilerplate.
31 Oct 2025, 19:55 UTC

Why async route handling matters
When you build a single‑page application you often need to load data before a view appears. Without a built‑in way to pause rendering, you end up showing a blank page or stale UI.
Mithril’s routing model
The core of Mithril’s navigation is m.route(). It registers a set of route objects that map URL patterns to component functions. Under the hood it uses the History API, so the browser’s back button works without extra code.
Each route entry looks like { path: '/user/:id', handler: User }. The handler is a function that returns a VDOM tree or a promise that resolves to one.
Promise‑driven component loading
Mithril treats a returned promise as a signal to wait. The framework suspends rendering until the promise settles, then re‑runs the diffing algorithm on the resolved VDOM. This means you can fetch data, wait for the server, and only render the component when the data is ready.
Because the promise is part of the route handler, you keep all navigation logic in one place and avoid manual “show loading spinner → fetch → render” boilerplate. Mithril’s small footprint — about 8 KB gzipped — makes this pattern cheap in performance‑critical apps.
Worked example
// 1. Define routes
m.route(document.body, '/', {
'/': Home,
'/user/:id': User
});
// 2. Component that returns a promise
function User(vm, args) {
const id = args.id;
return m('div', [
m('h2', `User ${id}`),
m('p', 'Loading...')
]);
}
// 3. Wrap the component to fetch data
function UserWithData(vm, args) {
const id = args.id;
// Simulate async fetch
return fetch(`https://api.example.com/users/${id}`)
.then(r => r.json())
.then(user => m('div', [
m('h2', `User ${id}`),
m('pre', JSON.stringify(user, null, 2))
]));
}
// 4. Register the promise‑based handler
m.route(document.body, '/', {
'/': Home,
'/user/:id': {
view: UserWithData // Mithril will wait for the promise
}
});
In this snippet the /user/:id route uses an object with a view property. The function UserWithData returns a promise from fetch. Mithril waits, then renders the user data once the request completes. If the URL does not match any defined path, Mithril stays on the current page, which can be confusing — always verify that your route patterns cover all expected URLs.
Verification steps
- Install the library:
npm install mithril. This requires write permission onnode_modules. - Run a minimal test page that includes the route definitions above. Open the page, navigate to
/user/42, and verify that the loading message disappears after a moment. - Check that the URL changes in the address bar and that the console shows no errors.
- If you need TypeScript typings, install the community package:
npm install @types/mithril. Note that definitions may lag behind the latest 1.x release. - Test with Mithril’s official examples at https://mithril.js.org/examples.html to see real‑world patterns.
Limitations
Mithril 1.x does not have official TypeScript support; the community @types/mithril package provides a best‑effort definition set that can be out‑of‑date. This may cause compile‑time mismatches when using newer JavaScript features.
The ecosystem is smaller than React or Vue, so third‑party components for advanced UI needs are scarce. You may need to write more custom code.
Server‑side rendering is not built‑in. You must add a separate build step (e.g., using m.render on the server) to generate HTML for initial loads.
Takeaway
Mithril’s promise‑driven routing gives you a concise way to load data before a component appears, keeping navigation code tidy and reducing boilerplate. By returning a promise from a route handler, you let the framework manage the loading state automatically. Verify the setup with a simple route, test the async flow, and be aware of the TypeScript and SSR caveats before committing to a production codebase.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.