The core HTM library provides the fundamental tagged template functionality that can be bound to any hyperscript-compatible function.
function bind<HResult>(
h: (type: any, props: Record<string, any>, ...children: any[]) => HResult
): (strings: TemplateStringsArray, ...values: any[]) => HResult | HResult[];Binds HTM to a hyperscript function and returns a tagged template function.
Parameters:
hh(type, props, ...children)typepropsnullchildrenReturns:
Usage Examples:
import htm from "htm";
// Custom hyperscript function
const h = (tag, props, ...children) => ({ tag, props, children });
const html = htm.bind(h);
// Basic element
const element = html`<div>Hello World</div>`;
// Result: { tag: 'div', props: null, children: ['Hello World'] }
// Element with props
const button = html`<button class="primary" onclick=${handleClick}>Click</button>`;
// Result: { tag: 'button', props: { class: 'primary', onclick: handleClick }, children: ['Click'] }
// Component usage
const MyComponent = ({ name }) => html`<span>Hello ${name}</span>`;
const app = html`<${MyComponent} name="Alice" />`;
// Result: { tag: MyComponent, props: { name: 'Alice' }, children: [] }// Self-closing
html`<div />`
html`<img src="photo.jpg" />`
// With content
html`<div>Content here</div>`
html`<h1>Title</h1>`
// Nested elements
html`
<div>
<h1>Title</h1>
<p>Paragraph</p>
</div>
`const className = "container";
const content = "Hello World";
const isVisible = true;
// Dynamic attributes
html`<div class=${className}>Content</div>`
// Dynamic content
html`<div>${content}</div>`
// Boolean attributes
html`<input disabled=${isVisible} />`
// Mixed static and dynamic
html`<div class="base ${className}">Content</div>`const MyComponent = ({ title, children }) => html`
<div class="component">
<h2>${title}</h2>
<div class="content">${children}</div>
</div>
`;
// Component usage
html`<${MyComponent} title="Welcome">
<p>Child content</p>
</${MyComponent}>`;
// Self-closing component
html`<${MyComponent} title="Welcome" />`;const props = { class: "button", disabled: true };
const extraProps = { "data-test": "my-button" };
// Spread props
html`<button ...${props} onclick=${handleClick}>Click</button>`;
// Multiple spreads
html`<button ...${props} ...${extraProps}>Click</button>`;
// Mix spread and direct props
html`<button ...${props} id="special">Click</button>`;// Returns array when multiple roots
const items = html`
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
`;
// Result: [{ tag: 'li', ... }, { tag: 'li', ... }, { tag: 'li', ... }]
// Fragment syntax
const fragment = html`<>
<h1>Title</h1>
<p>Content</p>
</>`;// HTML comments (ignored in output)
html`<!-- This is a comment --><div>Content</div>`;
// Implicit closing tags
html`<div>Content</>`; // Equivalent to <div>Content</div>
// Component end tags with explicit closing
html`<${Component} prop="value">
Child content
</${Component}>`;HTM automatically caches compiled templates for performance:
const html = htm.bind(h);
// First call compiles and caches
const element1 = html`<div class=${class1}>Content</div>`;
// Second call with same template reuses cached version
const element2 = html`<div class=${class2}>Content</div>`;The caching system:
WeakMapHTM optimizes templates by distinguishing static and dynamic parts:
// Mostly static - highly optimized
html`<div class="static">Static content</div>`;
// Dynamic parts tracked separately
html`<div class=${dynamic}>Content: ${content}</div>`;// Virtual DOM builder
const vdom = (tag, props, ...children) => ({ tag, props, children });
const htmlVdom = htm.bind(vdom);
// HTML string builder
const htmlString = (tag, props, ...children) => {
const attrs = props ? Object.entries(props)
.map(([k, v]) => ` ${k}="${v}"`).join('') : '';
const content = children.join('');
return `<${tag}${attrs}>${content}</${tag}>`;
};
const htmlStr = htm.bind(htmlString);
// Custom renderer with logging
const loggingH = (tag, props, ...children) => {
console.log('Creating element:', tag, props, children);
return { tag, props, children };
};
const htmlLog = htm.bind(loggingH);// Define your hyperscript return type
interface VNode {
tag: string | Function;
props: Record<string, any> | null;
children: any[];
}
// Type-safe hyperscript function
const h = (tag: any, props: Record<string, any> | null, ...children: any[]): VNode => ({
tag, props, children
});
// Bind with proper typing
const html = htm.bind(h);
// TypeScript will infer VNode | VNode[] return type
const element = html`<div>Typed content</div>`;// Main HTM interface
interface HTM {
bind<HResult>(
h: HyperscriptFunction<HResult>
): TemplateFunction<HResult>;
}
// Hyperscript function type
type HyperscriptFunction<T> = (
type: any,
props: Record<string, any> | null,
...children: any[]
) => T;
// Template function type
type TemplateFunction<T> = (
strings: TemplateStringsArray,
...values: any[]
) => T | T[];
// Internal caching types
interface TemplateCache extends Map<TemplateStringsArray, any> {}
interface GlobalCache extends Map<HyperscriptFunction<any>, TemplateCache> {}