HTM provides seamless integration with Preact through pre-configured imports that combine HTM's tagged template syntax with Preact's component system.
import { html, h, render, Component } from "htm/preact";import {
html, h, render, Component, createContext,
useState, useEffect, useReducer, useRef, useMemo, useCallback,
useContext, useLayoutEffect, useImperativeHandle, useDebugValue, useErrorBoundary
} from "htm/preact/standalone";declare const html: (strings: TemplateStringsArray, ...values: any[]) => VNode;Pre-bound HTM function configured for Preact's
hReturns:
VNodeUsage 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);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'));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;
}The standard integration re-exports all hooks from
preact/hooksimport { 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>
`;
};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;
}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'));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'));// 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 }>;
}