Skip to content

Instantly share code, notes, and snippets.

@kenwebb
Last active November 18, 2025 16:18
Show Gist options
  • Select an option

  • Save kenwebb/6d8fbf279088cc552494f12b87d7cbe8 to your computer and use it in GitHub Desktop.

Select an option

Save kenwebb/6d8fbf279088cc552494f12b87d7cbe8 to your computer and use it in GitHub Desktop.
preact - HTM
<?xml version="1.0" encoding="UTF-8"?>
<!--Xholon Workbook http://www.primordion.com/Xholon/gwt/ MIT License, Copyright (C) Ken Webb, Tue Nov 18 2025 11:18:05 GMT-0500 (Eastern Standard Time)-->
<XholonWorkbook>
<Notes><![CDATA[
Xholon
------
Title: preact - HTM
Description:
Url: http://www.primordion.com/Xholon/gwt/
InternalName: 6d8fbf279088cc552494f12b87d7cbe8
Keywords:
My Notes
--------
2025 Nov 16
In this workbook, I will construct a GUI using HTM, that will be useful with a Xholon app.
### References
(1) https://preactjs.com/guide/v10/no-build-workflows#htm
Whilst JSX is generally the most popular way to write Preact applications,
it requires a build step to convert the non-standard syntax into something browsers and other runtimes can understand natively.
Writing h/createElement calls by hand can be a bit tedious though with less than ideal ergonomics,
so we instead recommend a JSX-like alternative called HTM.
Instead of requiring a build step (though it can use one, see `babel-plugin-htm`),
HTM uses Tagged Templates syntax, a feature of JavaScript that's been around since 2015 and is supported in all modern browsers.
This is an increasingly popular way to write Preact apps and is likely the most popular for those choosing to forgo a build step.
HTM supports all standard Preact features, including Components, Hooks, Signals, etc.,
the only difference being the syntax used to write the "JSX" return value.
(2) https://github.com/developit/htm
HTM (Hyperscript Tagged Markup)
htm is JSX-like syntax in plain JavaScript - no transpiler necessary.
Develop with React/Preact directly in the browser, then compile htm away for production.
It uses standard JavaScript Tagged Templates and works in all modern browsers.
Syntax: like JSX but also lit
The syntax you write when using HTM is as close as possible to JSX:
Spread props: <div ...${props}> instead of <div {...props}>
Self-closing tags: <div />
Components: <${Foo}> instead of <Foo> (where Foo is a component reference)
Boolean attributes: <div draggable />
Improvements over JSX
htm actually takes the JSX-style syntax a couple steps further!
Here's some ergonomic features you get for free that aren't present in JSX:
No transpiler necessary
HTML's optional quotes: <div class=foo>
Component end-tags: <${Footer}>footer content<//>
Syntax highlighting and language support via the lit-html VSCode extension and vim-jsx-pretty plugin.
Multiple root element (fragments): <div /><div />
Support for HTML-style comments: <div><!-- comment --></div>
Installation
htm is published to npm, and accessible via the unpkg.com CDN:
via npm:
npm i htm
hotlinking from unpkg: (no build tool needed!)
import htm from 'https://unpkg.com/htm?module'
const html = htm.bind(React.createElement);
// just want htm + preact in a single file? there's a highly-optimized version of that:
import { html, render } from 'https://unpkg.com/htm/preact/standalone.module.js'
Usage
If you're using Preact or React, we've included off-the-shelf bindings to make your life easier. They also have the added benefit of sharing a template cache across all modules.
import { render } from 'preact';
import { html } from 'htm/preact';
render(html`<a href="/">Hello!</a>`, document.body);
Similarly, for React:
import ReactDOM from 'react-dom';
import { html } from 'htm/react';
ReactDOM.render(html`<a href="/">Hello!</a>`, document.body);
and more ...
(3) https://www.npmjs.com/package/htm
### Code from ref[1]
import { render } from 'preact';
import { useState } from 'preact/hooks';
import { html } from 'htm/preact';
function Counter() {
const [count, setCount] = useState(0);
return html`
<div class="counter-container">
<button onClick=${() => setCount(count + 1)}>Increment</button>
<input readonly value=${count} />
<button onClick=${() => setCount(count - 1)}>Decrement</button>
</div>
`;
}
render(html`<${Counter} />`, document.getElementById('app'));
()
]]></Notes>
<_-.XholonClass>
<!-- domain objects -->
<PhysicalSystem/>
<Block/>
<Brick/>
<!-- quantities -->
<Height superClass="Quantity"/>
</_-.XholonClass>
<xholonClassDetails>
<Block>
<port name="height" connector="Height"/>
</Block>
</xholonClassDetails>
<PhysicalSystem>
<Block>
<Height>0.1 m</Height>
</Block>
<Brick multiplicity="2"/>
</PhysicalSystem>
<Blockbehavior implName="org.primordion.xholon.base.Behavior_gwtjs"><![CDATA[
var a = 123;
var b = 456;
var c = a * b;
if (console) {
console.log(c);
}
//# sourceURL=Blockbehavior.js
]]></Blockbehavior>
<Heightbehavior implName="org.primordion.xholon.base.Behavior_gwtjs"><![CDATA[
var myHeight, testing;
var beh = {
postConfigure: function() {
testing = Math.floor(Math.random() * 10);
myHeight = this.cnode.parent();
},
act: function() {
myHeight.println(this.toString());
},
toString: function() {
return "testing:" + testing;
}
}
//# sourceURL=Heightbehavior.js
]]></Heightbehavior>
<Brickbehavior implName="org.primordion.xholon.base.Behavior_gwtjs"><![CDATA[
$wnd.xh.Brickbehavior = function Brickbehavior() {}
$wnd.xh.Brickbehavior.prototype.postConfigure = function() {
this.brick = this.cnode.parent();
this.iam = " red brick";
};
$wnd.xh.Brickbehavior.prototype.act = function() {
this.brick.println("I am a" + this.iam);
};
//# sourceURL=Brickbehavior.js
]]></Brickbehavior>
<Brickbehavior implName="org.primordion.xholon.base.Behavior_gwtjs"><![CDATA[
console.log("I'm another brick behavior");
]]></Brickbehavior>
<SvgClient><Attribute_String roleName="svgUri"><![CDATA[data:image/svg+xml,
<svg width="100" height="50" xmlns="http://www.w3.org/2000/svg">
<g>
<title>Block</title>
<rect id="PhysicalSystem/Block" fill="#98FB98" height="50" width="50" x="25" y="0"/>
<g>
<title>Height</title>
<rect id="PhysicalSystem/Block/Height" fill="#6AB06A" height="50" width="10" x="80" y="0"/>
</g>
</g>
</svg>
]]></Attribute_String><Attribute_String roleName="setup">${MODELNAME_DEFAULT},${SVGURI_DEFAULT}</Attribute_String></SvgClient>
</XholonWorkbook>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment