Sign Up for Free

RunKit +

Try any Node.js package right in your browser

This is a playground to test code. It runs a full Node.js environment and already has all of npm’s 400,000 packages pre-installed, including htm with all npm packages installed. Try it out:

var htm = require("htm")

This service is provided by RunKit and is not affiliated with npm, Inc or the package authors.

htm v3.0.4

The Tagged Template syntax for Virtual DOM. Only browser-compatible syntax.

HTM (Hyperscript Tagged Markup) npm

hyperscript tagged markup demo

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.

htm by the numbers:

🐣 < 600 bytes when used directly in the browser

⚛️ < 500 bytes when used with Preact (thanks gzip 🌈)

🥚 < 450 byte htm/mini version

🏅 0 bytes if compiled using babel-plugin-htm

Syntax: like JSX but also lit

The syntax you write when using HTM is as close as possible to JSX:

  • Spread props: <div ...${props}>
  • Self-closing tags: <div />
  • Components: <${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>


htm is published to npm, and accessible via the CDN:

via npm:

npm i htm

hotlinking from unpkg: (no build tool needed!)

import htm from ''
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 ''


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);

Advanced Usage

Since htm is a generic library, we need to tell it what to "compile" our templates to. You can bind htm to any function of the form h(type, props, ...children) _(hyperscript)_. This function can return anything - htm never looks at the return value.

Here's an example h() function that returns tree nodes:

function h(type, props, ...children) {
  return { type, props, children };

To use our custom 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(type, props, ...children) {
  return { type, props, children };

const html = htm.bind(h);

console.log( html`<h1 id=hello>Hello world!</h1>` );
// {
//   type: 'h1',
//   props: { id: 'hello' },
//   children: ['Hello world!']
// }

If the template has multiple element at the root level the output is an array of h results:

  <h1 id=hello>Hello</h1>
  <div class=world>World!</div>
// [
//   {
//     type: 'h1',
//     props: { id: 'hello' },
//     children: ['Hello']
//   },
//   {
//     type: 'div',
//     props: { class: 'world' },
//     children: ['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 '';

    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})" />
              ${ => html`
            <button onClick=${() => this.addTodo()}>Add Todo</button>
            <${Footer}>footer content here<//>

    const Header = ({ name }) => html`<h1>${name} List</h1>`

    const Footer = props => html`<footer ...${props} />`

    render(html`<${App} page="All" />`, document.body);

⚡️ See live version

⚡️ Try this on CodeSandbox

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);

Other Uses

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) (never do this)

import htm from 'htm';
import jsxobj from 'jsxobj';

const html = htm.bind(jsxobj);

  <webpack watch mode=production>
    <entry path="src/index.js" />
// {
//   watch: true,
//   mode: 'production',
//   entry: {
//     path: 'src/index.js'
//   }
// }

Demos & Examples

Project Status

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. The technique turns out to be framework-agnostic, so it should work great with any library or renderer that works with JSX.

htm is stable, fast, well-tested and ready for production use.


RunKit is a free, in-browser JavaScript dev environment for prototyping Node.js code, with every npm package installed. Sign up to share your code.
Sign Up for Free