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's built using Tagged Templates and the browser's HTML parser.
🐣 700 bytes when used directly in the browser
⚛️ 500 bytes when used with Preact (thanks gzip 🌈)
🏅 0 bytes when compiled using babel-plugin-htm
The syntax is inspired by lit-html, but includes features familiar to anyone who works with JSX:
- Rest spread: 
<div ...${props}> - Self-closing tags: 
<div /> - Components: 
<${Foo}>(whereFoois a component reference) - Boolean attributes: 
<div draggable /> 
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> - HTML's self-closing tags: 
<img src=${url}> - Optional end-tags: 
<section><h1>this is the whole template! - Component end-tags: 
<${Footer}>footer content<//> - Support for HTML comments: 
<div><!-- don't delete this! --></div> - Syntax highlighting and language support via the lit-html VSCode extension.
 
The original goal for htm was to create a wrapper around Preact that felt natural for use untranspiled in the browser. I wanted to use Virtual DOM, but I wanted to eschew build tooling and use ES Modules directly.
This meant giving up JSX, and the closest alternative was Tagged Templates. So, I wrote this library to patch up the differences between the two as much as possible. As it turns out, the technique is framework-agnostic, so it should work great with most Virtual DOM libraries.
htm is published to npm, and accessible via the unpkg.com CDN:
via npm:
npm i htmhotlinking 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.mjs'Since htm is a generic library, we need to tell it what to "compile" our templates to.
The target should be a function of the form h(tag, props, ...children) (hyperscript), and can return anything.
// this is our hyperscript function. for now, it just returns a description object.
function h(tag, props, ...children) {
  return { tag, props, children };
}To use that h() function, we need to create our own html tag function by binding htm to our h() function:
import htm from 'htm';
const html = htm.bind(h);Now we have an html() template tag that can be used to produce objects in the format we created above.
Here's the whole thing for clarity:
import htm from 'htm';
function h(tag, props, ...children) {
  return { tag, props, children };
}
const html = htm.bind(h);
console.log( html`<h1 id=hello>Hello world!</h1>` );
// {
//   tag: 'h1',
//   props: { id: 'hello' },
//   children: ['Hello world!']
// }Curious to see what it all looks like? Here's a working app!
It's a single HTML file, and there's no build or tooling. You can edit it with nano.
<!DOCTYPE html>
<html lang="en">
  <title>htm Demo</title>
  <script type="module">
    import { html, Component, render } from 'https://unpkg.com/htm/preact/standalone.mjs';
    class App extends Component {
      addTodo() {
        const { todos = [] } = this.state;
        this.setState({ todos: todos.concat(`Item ${todos.length}`) });
      }
      render({ page }, { todos = [] }) {
        return html`
          <div class="app">
            <${Header} name="ToDo's (${page})" />
            <ul>
              ${todos.map(todo => html`
                <li>${todo}</li>
              `)}
            </ul>
            <button onClick=${() => this.addTodo()}>Add Todo</button>
            <${Footer}>footer content here<//>
          </div>
        `;
      }
    }
		  
    const Header = ({ name }) => html`<h1>${name} List</h1>`
    const Footer = props => html`<footer ...${props} />`
    render(html`<${App} page="All" />`, document.body);
  </script>
</html>Here's a live version.
How nifty is that? Notice there's only one import - here we're using the prebuilt Preact integration since it's easier to import and a bit smaller.
The same example works fine without the prebuilt version, just using two imports:
import { h, Component, render } from 'preact';
import htm from 'htm';
const html = htm.bind(h);
render(html`<${App} page="All" />`, document.body);Since htm is designed to meet the same need as JSX, you can use it anywhere you'd use JSX.
Generate HTML using vhtml:
import htm from 'htm';
import vhtml from 'vhtml';
const html = htm.bind(vhtml);
console.log( html`<h1 id=hello>Hello world!</h1>` );
// '<h1 id="hello">Hello world!</h1>'Webpack configuration via jsxobj: (details here)
import htm from 'htm';
import jsxobj from 'jsxobj';
const html = htm.bind(jsxobj);
console.log(html`
  <webpack watch mode=production>
    <entry path="src/index.js" />
  </webpack>
`);
// {
//   watch: true,
//   mode: 'production',
//   entry: {
//     path: 'src/index.js'
//   }
// }