CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-htm

JSX-like syntax using tagged template literals for Virtual DOM without transpilation

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

advanced-usage.mddocs/

Advanced Usage

HTM provides advanced customization through custom hyperscript functions and integration with build tools.

Custom Hyperscript Functions

HTM's core strength is its ability to work with any hyperscript-compatible function.

HTML String Generator

import htm from "htm";

const htmlString = (tag, props, ...children) => {
  const attrs = props ? 
    Object.entries(props)
      .filter(([key, value]) => value !== false)
      .map(([key, value]) => value === true ? key : `${key}="${value}"`)
      .join(' ') : '';
  
  const attrsStr = attrs ? ` ${attrs}` : '';
  const content = children.join('');
  
  if (children.length === 0 && isSelfClosing(tag)) {
    return `<${tag}${attrsStr} />`;
  }
  
  return `<${tag}${attrsStr}>${content}</${tag}>`;
};

const isSelfClosing = (tag) => 
  ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 
   'link', 'meta', 'param', 'source', 'track', 'wbr'].includes(tag);

const html = htm.bind(htmlString);

const markup = html`
  <div class="container">
    <h1>Title</h1>
    <img src="photo.jpg" alt="Photo" />
  </div>
`;

console.log(markup);
// <div class="container"><h1>Title</h1><img src="photo.jpg" alt="Photo" /></div>

JSON Structure Generator

import htm from "htm";

const jsonBuilder = (tag, props, ...children) => ({
  type: 'element',
  tagName: tag,
  attributes: props || {},
  children: children.map(child => 
    typeof child === 'string' ? 
      { type: 'text', content: child } : 
      child
  )
});

const html = htm.bind(jsonBuilder);

const structure = html`
  <article class="post">
    <h1>Blog Title</h1>
    <p>Content here...</p>
  </article>
`;

console.log(JSON.stringify(structure, null, 2));

CSS-in-JS Integration

import htm from "htm";
import styled from "styled-components";

// Use HTM with styled-components
const styledHtml = htm.bind(styled);

const StyledButton = styledHtml`
  button\`
    background: \${primary};
    color: white;
    border: none;
    padding: 10px 20px;
    border-radius: 4px;
    cursor: pointer;
    
    &:hover \{
      opacity: 0.8;
    \}
  \`
`;

Build-time Optimizations

Babel Plugin Integration

HTM can be compiled at build time using Babel plugins:

// babel.config.js
module.exports = {
  plugins: [
    ["babel-plugin-htm", {
      "pragma": "React.createElement",
      "pragmaFrag": "React.Fragment"
    }]
  ]
};

// Before transformation
const element = html`<div>Hello ${name}</div>`;

// After transformation  
const element = React.createElement("div", null, "Hello ", name);

Template Validation

// Validate template syntax at runtime
function validateHTMLTemplate(template, values = []) {
  try {
    const html = htm.bind((tag, props, ...children) => ({ tag, props, children }));
    const result = html(template, ...values);
    return { valid: true, result };
  } catch (error) {
    return { 
      valid: false, 
      error: error.message,
      suggestion: getSuggestion(error.message)
    };
  }
}

function getSuggestion(errorMessage) {
  if (errorMessage.includes('Unexpected')) {
    return 'Check for mismatched tags or invalid syntax';
  }
  if (errorMessage.includes('property')) {
    return 'Verify all interpolated values are properly closed';
  }
  return 'Review template syntax and ensure proper nesting';
}

// Usage
const result = validateHTMLTemplate`<div><span></div>`; // Mismatched tags
if (!result.valid) {
  console.error('Template error:', result.error);
  console.log('Suggestion:', result.suggestion);
}

Performance Optimization

Template Caching

HTM uses internal caching for performance. You can inspect and manage caches:

import htm from "htm";

// Access internal cache (for debugging/profiling)
const html = htm.bind(h);

// Templates are cached by their static strings
const template1 = html`<div>${value1}</div>`;
const template2 = html`<div>${value2}</div>`; // Same cache entry

// Different static parts create different cache entries  
const template3 = html`<span>${value1}</span>`; // New cache entry

Mini Version

Use the mini version for smaller bundle sizes:

// Regular version (~600 bytes)
import htm from "htm";

// Mini version (~400 bytes) 
import htm from "htm/mini";

// Same API, slightly different internal representation
const html = htm.bind(h);

TypeScript Integration

Advanced Type Safety

import htm from "htm";

// Define strict return types
interface VNode {
  tag: string | Function;
  props: Record<string, any> | null;
  children: any[];
}

// Type-safe hyperscript function
const h = (
  tag: string | Function, 
  props: Record<string, any> | null, 
  ...children: any[]
): VNode => ({
  tag, props, children
});

// Bind with proper typing
const html = htm.bind(h);

// TypeScript infers VNode | VNode[] return type
const element = html`<div>Content</div>`;

// Component with props validation
interface ButtonProps {
  variant: 'primary' | 'secondary';
  disabled?: boolean;
  onClick: () => void;
}

const Button: React.FC<ButtonProps> = ({ variant, disabled, onClick, children }) => {
  return html`
    <button 
      class="btn btn-${variant}"
      disabled=${disabled}
      onClick=${onClick}
    >
      ${children}
    </button>
  `;
};

Custom Template Types

// Create typed template functions
type HTMLTemplate<T> = (strings: TemplateStringsArray, ...values: any[]) => T;

// Custom hyperscript with specific return type
interface CustomElement {
  type: string;
  attributes: Record<string, any>;
  content: string;
}

const customH = (tag: string, props: any, ...children: any[]): CustomElement => ({
  type: tag,
  attributes: props || {},
  content: children.join('')
});

const customHtml: HTMLTemplate<CustomElement> = htm.bind(customH);

// Type-safe usage
const element: CustomElement = customHtml`<div class="test">Content</div>`;

Types

// Advanced hyperscript types
type CustomHyperscript<T> = (
  type: any,
  props: Record<string, any> | null,
  ...children: any[]
) => T;

type CustomTemplate<T> = (
  strings: TemplateStringsArray,
  ...values: any[]
) => T | T[];

// Custom element structures
interface CustomVNode {
  type: string;
  attributes: Record<string, any>;
  content: string;
}

interface CustomElement {
  tag: string;
  props: Record<string, any>;
  children: any[];
}

// Validation result types
interface ValidationResult {
  valid: boolean;
  result?: any;
  error?: string;
  suggestion?: string;
}

docs

advanced-usage.md

core-htm.md

index.md

preact-integration.md

react-integration.md

tile.json