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

preact-integration.mddocs/

Preact Integration

HTM provides seamless integration with Preact through pre-configured imports that combine HTM's tagged template syntax with Preact's component system.

Import Paths

Standard Preact Integration

import { html, h, render, Component } from "htm/preact";

Standalone Preact Integration (with hooks)

import { 
  html, h, render, Component, createContext,
  useState, useEffect, useReducer, useRef, useMemo, useCallback,
  useContext, useLayoutEffect, useImperativeHandle, useDebugValue, useErrorBoundary
} from "htm/preact/standalone";

API Reference

html (Tagged Template Function)

declare const html: (strings: TemplateStringsArray, ...values: any[]) => VNode;

Pre-bound HTM function configured for Preact's h function.

Returns:

  • VNode - Preact virtual DOM node

Usage Examples:

import { html, render } from "htm/preact";

// Basic element
const element = html`<div class="container">Hello World</div>`;

// Dynamic content
const name = "Alice";
const greeting = html`<h1>Hello, ${name}!</h1>`;

// Event handlers
const handleClick = () => console.log('Clicked!');
const button = html`<button onclick=${handleClick}>Click me</button>`;

// Render to DOM
render(greeting, document.body);

Component Integration

import { html, Component, render } from "htm/preact";

// Functional component
const Welcome = ({ name }) => html`
  <div class="welcome">
    <h1>Welcome, ${name}!</h1>
  </div>
`;

// Class component
class Counter extends Component {
  constructor() {
    super();
    this.state = { count: 0 };
  }
  
  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };
  
  render() {
    return html`
      <div class="counter">
        <p>Count: ${this.state.count}</p>
        <button onclick=${this.increment}>+</button>
      </div>
    `;
  }
}

// Usage
const app = html`
  <div>
    <${Welcome} name="Alice" />
    <${Counter} />
  </div>
`;

render(app, document.getElementById('root'));

Preact Re-exports

These functions are re-exported directly from Preact:

// Hyperscript function
declare function h(type: any, props: any, ...children: any[]): VNode;

// Render function
declare function render(tree: VNode, parent: HTMLElement): void;

// Component base class
declare class Component {
  constructor(props?: any, context?: any);
  setState(partial: any, callback?: () => void): void;
  forceUpdate(callback?: () => void): void;
  render(props?: any, state?: any, context?: any): VNode | null;
  componentDidMount?(): void;
  componentWillUnmount?(): void;
  componentDidUpdate?(previousProps: any, previousState: any, snapshot: any): void;
  shouldComponentUpdate?(nextProps: any, nextState: any, nextContext: any): boolean;
  getSnapshotBeforeUpdate?(previousProps: any, previousState: any): any;
  componentDidCatch?(error: any, errorInfo: any): void;
}

Hooks Integration

Standard Integration (htm/preact)

The standard integration re-exports all hooks from preact/hooks:

import { html, useState, useEffect, useRef } from "htm/preact";
// Note: hooks are available via re-export from preact/hooks

const HookComponent = () => {
  const [count, setCount] = useState(0);
  const buttonRef = useRef();
  
  useEffect(() => {
    console.log('Count changed:', count);
  }, [count]);
  
  return html`
    <div>
      <p>Count: ${count}</p>
      <button ref=${buttonRef} onclick=${() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  `;
};

Standalone Integration (htm/preact/standalone)

The standalone version includes all hooks as direct exports:

// State hooks
declare function useState<T>(initialState: T | (() => T)): [T, (value: T | ((prev: T) => T)) => void];
declare function useReducer<T, A>(
  reducer: (state: T, action: A) => T,
  initialState: T
): [T, (action: A) => void];

// Effect hooks  
declare function useEffect(effect: () => void | (() => void), deps?: any[]): void;
declare function useLayoutEffect(effect: () => void | (() => void), deps?: any[]): void;

// Ref hooks
declare function useRef<T>(initialValue: T): { current: T };
declare function useImperativeHandle<T>(
  ref: any,
  createHandle: () => T,
  deps?: any[]
): void;

// Memoization hooks
declare function useMemo<T>(factory: () => T, deps: any[]): T;
declare function useCallback<T extends (...args: any[]) => any>(
  callback: T,
  deps: any[]
): T;

// Context hooks
declare function useContext<T>(context: Context<T>): T;

// Development hooks
declare function useDebugValue(value: any, formatter?: (value: any) => any): void;
declare function useErrorBoundary(): [any, () => void];

// Context creation
declare function createContext<T>(defaultValue: T): Context<T>;

interface Context<T> {
  Provider: any;
  Consumer: any;
}

Complete Usage Examples

Todo App with Hooks

import { html, render, useState, useEffect } from "htm/preact/standalone";

const TodoApp = () => {
  const [todos, setTodos] = useState([]);
  const [input, setInput] = useState('');
  
  const addTodo = () => {
    if (input.trim()) {
      setTodos([...todos, { id: Date.now(), text: input, done: false }]);
      setInput('');
    }
  };
  
  const toggleTodo = (id) => {
    setTodos(todos.map(todo => 
      todo.id === id ? { ...todo, done: !todo.done } : todo
    ));
  };
  
  const removeTodo = (id) => {
    setTodos(todos.filter(todo => todo.id !== id));
  };
  
  useEffect(() => {
    console.log(`${todos.length} todos`);
  }, [todos.length]);
  
  return html`
    <div class="todo-app">
      <h1>Todo App</h1>
      <div class="input-section">
        <input 
          type="text"
          value=${input}
          onInput=${(e) => setInput(e.target.value)}
          placeholder="Add a todo..."
        />
        <button onclick=${addTodo}>Add</button>
      </div>
      <ul class="todo-list">
        ${todos.map(todo => html`
          <li key=${todo.id} class=${todo.done ? 'done' : ''}>
            <span onclick=${() => toggleTodo(todo.id)}>
              ${todo.text}
            </span>
            <button onclick=${() => removeTodo(todo.id)}>×</button>
          </li>
        `)}
      </ul>
    </div>
  `;
};

render(html`<${TodoApp} />`, document.getElementById('root'));

Context and Custom Hooks

import { html, render, createContext, useContext, useState, useEffect } from "htm/preact/standalone";

// Create theme context
const ThemeContext = createContext({ theme: 'light', toggleTheme: () => {} });

// Custom hook
const useLocalStorage = (key, initialValue) => {
  const [value, setValue] = useState(() => {
    const saved = localStorage.getItem(key);
    return saved ? JSON.parse(saved) : initialValue;
  });
  
  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);
  
  return [value, setValue];
};

// Theme provider component
const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useLocalStorage('theme', 'light');
  
  const toggleTheme = () => {
    setTheme(theme === 'light' ? 'dark' : 'light');
  };
  
  return html`
    <${ThemeContext.Provider} value=${{ theme, toggleTheme }}>
      <div class="app ${theme}">
        ${children}
      </div>
    </${ThemeContext.Provider}>
  `;
};

// Component using context
const ThemedButton = ({ children }) => {
  const { theme, toggleTheme } = useContext(ThemeContext);
  
  return html`
    <button 
      class="themed-button ${theme}"
      onclick=${toggleTheme}
    >
      ${children} (${theme} mode)
    </button>
  `;
};

// App
const App = () => html`
  <${ThemeProvider}>
    <h1>Themed App</h1>
    <${ThemedButton}>Toggle Theme<//>
  </${ThemeProvider}>
`;

render(html`<${App} />`, document.getElementById('root'));

Types

// Preact VNode type
interface VNode<P = any> {
  type: any;
  props: P & { children?: any };
  key?: any;
  ref?: any;
  startTime?: number;
  endTime?: number;
}

// HTML template function type
type HTMLTemplate = (strings: TemplateStringsArray, ...values: any[]) => VNode;

// Component types
type FunctionalComponent<P = {}> = (props: P & { children?: any }) => VNode | null;

interface ComponentClass<P = {}, S = {}> {
  new (props: P, context?: any): Component<P, S>;
}

// Hook types
type StateUpdater<T> = (value: T | ((prevState: T) => T)) => void;
type EffectCallback = () => void | (() => void);
type DependencyList = ReadonlyArray<any>;

// Context type
interface Context<T> {
  Provider: ComponentClass<{ value: T }>;
  Consumer: ComponentClass<{ children: (value: T) => VNode }>;
}

docs

advanced-usage.md

core-htm.md

index.md

preact-integration.md

react-integration.md

tile.json