oninit vs oncreate for State Initialization in Mithril 2.x
0 reputation · 27 May 2026, 19:35 UTC
0 reputation · 27 May 2026, 19:35 UTC
When initializing a component's state in Mithril 2.x, developers must choose between the oninit and oncreate lifecycle hooks. The choice depends on whether the initialization logic requires access to the rendered DOM or must be completed before the first render cycle occurs.
Using oninit allows for state setup before the virtual DOM is rendered, preventing potential layout shifts or empty states during the first paint. However, it lacks access to the actual DOM elements. Conversely, oncreate provides a reference to the inserted DOM node, which is necessary for third-party library integration or element measurement, but any state changes triggered here will immediately force a second re-render of the component.
For a component that requires both initial state and a DOM-based measurement to calculate that state, which hook provides the most efficient execution path? Is there a documented strategy to avoid the double-render penalty when oncreate is used for state initialization?
31275 reputation · 28 May 2026, 02:30 UTC
In Mithril 2.x the two hooks that can be used for state setup are oninit and oncreate.
oninit runs before any virtual‑DOM diffing, so the component has no access to real DOM nodes.oncreate runs after the node is inserted into the real DOM, giving you a reference to the element for measurements or third‑party library hooks.oninitIf the state can be derived purely from props, query parameters, or a synchronous calculation that does not require the element’s size, oninit is the safest choice. It guarantees a single render and avoids any layout shift.
oncreate is requiredWhen the component must read offsetWidth, clientHeight, or any other real‑DOM property to compute its initial state, oncreate is the only hook that exposes the node. The standard pattern looks like this:
const MyComp = {
oncreate(vnode) {
const width = vnode.dom.offsetWidth;
vnode.state.calculated = width * 0.8; // example
m.redraw(); // forces a second render
},
view(vnode) {
return m('div', {style:{width: vnode.state.calculated + 'px'}}, 'content');
}
};
Because m.redraw() is implicit after oncreate, the component will render twice: once with the placeholder values and once with the measured state.
The double‑render penalty is unavoidable if you need the DOM before the first paint. However, Mithril provides a practical way to keep the user from seeing the intermediate state:
view return a minimal element (e.g., a <span> or empty <div>) that takes up the same space as the final component or is hidden with CSS.oncreate. Use vnode.dom to read the size, compute the state, and store it in vnode.state.vnode.state.ready = true) and return the full component in view when ready is true. Because the flag is set after the first render, Mithril will only schedule one additional redraw, which renders the fully‑measured component.
Example:
const Measured = {
oncreate(vnode) {
const width = vnode.dom.offsetWidth;
vnode.state.width = width * 0.8;
vnode.state.ready = true; // triggers one redraw
},
view(vnode) {
if (!vnode.state.ready) {
return m('span', {style:{display:'inline-block',width:'100%'}}, ''); // placeholder
}
return m('div', {style:{width:vnode.state.width + 'px'}}, 'Measured content');
}
};
Key points:
oncreate, Mithril still queues a redraw, but only one extra render occurs after the placeholder is removed.Use oninit when you can compute state without the real DOM. When DOM measurement is mandatory, render a lightweight placeholder, perform the measurement in oncreate, set a flag, and let Mithril do a single redraw. This pattern sidesteps the double‑render visual flicker while still leveraging the real‑DOM data you need.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.