Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/developit/htm
Hyperscript Tagged Markup: JSX alternative using standard tagged templates, with compiler support.
https://github.com/developit/htm
babel babel-plugin jsx tagged-template virtual-dom
Last synced: 3 days ago
JSON representation
Hyperscript Tagged Markup: JSX alternative using standard tagged templates, with compiler support.
- Host: GitHub
- URL: https://github.com/developit/htm
- Owner: developit
- License: apache-2.0
- Created: 2018-07-30T19:04:49.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2024-02-01T21:36:52.000Z (10 months ago)
- Last Synced: 2024-12-09T05:38:10.817Z (3 days ago)
- Topics: babel, babel-plugin, jsx, tagged-template, virtual-dom
- Language: JavaScript
- Homepage:
- Size: 368 KB
- Stars: 8,739
- Watchers: 72
- Forks: 170
- Open Issues: 42
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
- awesome - developit/htm - Hyperscript Tagged Markup: JSX alternative using standard tagged templates, with compiler support. (JavaScript)
- awesome - htm - Hyperscript Tagged Markup: JSX alternative using standard tagged templates, with compiler support. (JavaScript)
- awesome-repositories - developit/htm - Hyperscript Tagged Markup: JSX alternative using standard tagged templates, with compiler support. (JavaScript)
- awesome-repositories - developit/htm
- awesome-web-react - HTM (Hyperscript Tagged Markup)
- awesome-list - htm
- awesome-lit - htm - Hyperscript Tagged Markup: JSX alternative using standard tagged templates, with compiler support. (Similar libraries / Other Tools)
README
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].
## `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: `
` instead of ``
- Self-closing tags: ``
- Components: `<${Foo}>` instead of `` _(where `Foo` is a component reference)_
- Boolean attributes: ``## 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: ``
- 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): `
`
- Support for HTML-style comments: ``## Installation
`htm` is published to npm, and accessible via the unpkg.com CDN:
**via npm:**
```js
npm i htm
```**hotlinking from unpkg:** _(no build tool needed!)_
```js
import htm from 'https://unpkg.com/htm?module'
const html = htm.bind(React.createElement);
``````js
// 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.```js
import { render } from 'preact';
import { html } from 'htm/preact';
render(html`Hello!`, document.body);
```Similarly, for React:
```js
import ReactDOM from 'react-dom';
import { html } from 'htm/react';
ReactDOM.render(html`Hello!`, 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:
```js
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:
```js
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:
```js
import htm from 'htm';function h(type, props, ...children) {
return { type, props, children };
}const html = htm.bind(h);
console.log( html`
Hello world!
` );
// {
// 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:```js
console.log(html`
Hello
World!
`);
// [
// {
// type: 'h1',
// props: { id: 'hello' },
// children: ['Hello']
// },
// {
// type: 'div',
// props: { class: 'world' },
// children: ['world!']
// }
// ]
```### Caching
The default build of `htm` caches template strings, which means that it can return the same Javascript object at multiple points in the tree. If you don't want this behaviour, you have three options:
* Change your `h` function to copy nodes when needed.
* Add the code `this[0] = 3;` at the beginning of your `h` function, which disables caching of created elements.
* Use `htm/mini`, which disables caching by default.## Example
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.
```html
htm Demo
import { html, Component, render } from 'https://unpkg.com/htm/preact/standalone.module.js';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 key=${todo}>${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);
```
[⚡️ **See live version** ▶](https://htm-demo-preact.glitch.me/)
[⚡️ **Try this on CodeSandbox** ▶](https://codesandbox.io/s/x7pmq32j6q)
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:
```js
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]:**
```js
import htm from 'htm';
import vhtml from 'vhtml';const html = htm.bind(vhtml);
console.log( html`
Hello world!
` );
// 'Hello world!
'
```**Webpack configuration via [jsxobj]:** ([details here](https://webpack.js.org/configuration/configuration-languages/#babel-and-jsx)) _(never do this)_
```js
import htm from 'htm';
import jsxobj from 'jsxobj';const html = htm.bind(jsxobj);
console.log(html`
`);
// {
// watch: true,
// mode: 'production',
// entry: {
// path: 'src/index.js'
// }
// }
```## Demos & Examples
- [Canadian Holidays](https://github.com/pcraig3/hols): A full app using HTM and Server-Side Rendering
- [HTM SSR Example](https://github.com/timarney/htm-ssr-demo): Shows how to do SSR with HTM
- [HTM + Preact SSR Demo](https://gist.github.com/developit/699c8d8f180a1e4eed58167f9c6711be)
- [HTM + vhtml SSR Demo](https://gist.github.com/developit/ff925c3995b4a129b6b977bf7cd12ebd)## 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.
[Tagged Templates]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates
[lit-html]: https://github.com/Polymer/lit-html
[babel-plugin-htm]: https://github.com/developit/htm/tree/master/packages/babel-plugin-htm
[lit-html VSCode extension]: https://marketplace.visualstudio.com/items?itemName=bierner.lit-html
[vim-jsx-pretty plugin]: https://github.com/MaxMEllon/vim-jsx-pretty
[vhtml]: https://github.com/developit/vhtml
[jsxobj]: https://github.com/developit/jsxobj
[hyperscript]: https://github.com/hyperhype/hyperscript
[all modern browsers]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Browser_compatibility