A little bit of plain JavaScript can do a lot
I agree about the nuisance of creating DOM elements. innerHTML is OK if you’re doing static content, but for anything that needs to be dynamic (untrusted input, event handlers, etc.) I have a little tiny helper library that I carry around in my head and write into projects that need it:<p><pre><code>    const $T = text =&gt; document.createTextNode(text)
    
    function $E(tag, props, kids) {
        const elem = document.createElement(tag)
        for (const k in props) {
            elem[k] = props[k]
        }
        for (const kid of kids) {
            elem.appendChild(kid)
        }
        return elem
    }
</code></pre>
(Add flourishes as necessary for things like optional arguments and allowing falsey kids.)<p>This isn’t as nice as JSX, but it makes a reasonably ergonomic API for quick little projects. Here’s what the example in the article would look like (admittedly it’s a bit easier to follow with syntax highlighting):<p><pre><code>    const button = $E(&#x27;button&#x27;, {}, [
        $E(&#x27;i&#x27;, {className: &#x27;icon-lightbulb&#x27;}, []),
        $T(&#x27;I learned something!&#x27;),
        $E(&#x27;object&#x27;, {data: &#x27;&#x2F;confetti.svg&#x27;, width: 30, height: 30}, []),
    ]);</code></pre>