# Preact User Guide

Preact is a fast **4 kB** alternative to React with the same modern API. It gives you the full power of Virtual DOM components — JSX, hooks, function and class components, Context, refs, Suspense, and server-side rendering — without the bundle-size overhead. Through the optional `preact/compat` alias it can also act as a drop-in replacement for React in existing applications.

This guide covers everything you need to build user interfaces with Preact: installation, the rendering model, every public function and hook, the React-compatibility layer, debugging, server-side rendering, and a thorough troubleshooting reference. All commands, function signatures, and error messages here are taken directly from the Preact source.

> [!IMPORTANT]
> This guide documents **Preact v11.0.0-beta.1**, the in-development next major release (the `main` branch). The v11 line is a pre-release and its API surface may still change before a stable release. If you are maintaining a production app on the current stable line, you most likely want **Preact 10** (the `v10.x` branch / `preact@^10` on npm). Differences that matter to v10 users are flagged throughout with a "v11" note.

---

## Table of Contents

1. [Quick Start](#1-quick-start)
2. [Installation](#2-installation)
3. [Core Concepts](#3-core-concepts)
4. [Rendering: `render` and `hydrate`](#4-rendering-render-and-hydrate)
5. [Creating Elements: `h` / `createElement` and JSX](#5-creating-elements-h--createelement-and-jsx)
6. [Components](#6-components)
7. [Hooks (`preact/hooks`)](#7-hooks-preacthooks)
8. [Context](#8-context)
9. [Refs](#9-refs)
10. [The React Compatibility Layer (`preact/compat`)](#10-the-react-compatibility-layer-preactcompat)
11. [Suspense and Lazy Loading](#11-suspense-and-lazy-loading)
12. [Server-Side Rendering](#12-server-side-rendering)
13. [Debugging (`preact/debug`) and DevTools](#13-debugging-preactdebug-and-devtools)
14. [Testing (`preact/test-utils`)](#14-testing-preacttest-utils)
15. [Common Tasks (Recipes)](#15-common-tasks-recipes)
16. [Troubleshooting](#16-troubleshooting)
17. [FAQ](#17-faq)
18. [API Quick Reference](#18-api-quick-reference)

---

## 1. Quick Start

Install Preact and render your first component into the page.

```bash
npm install preact
```

```jsx
import { render } from 'preact';
import { useState } from 'preact/hooks';

function Counter() {
	const [count, setCount] = useState(0);

	return (
		<div>
			<p>Count: {count}</p>
			<button onClick={() => setCount(count + 1)}>Increment</button>
		</div>
	);
}

render(<Counter />, document.getElementById('app'));
```

```text
Count: 0
[ Increment ]
```

Each click re-renders the `Counter` and Preact applies the minimum number of DOM operations needed to reflect the new state.

> [!TIP]
> Preact's public API is intentionally tiny. The core package exports only `render`, `hydrate`, `createElement`/`h`, `Fragment`, `Component`, `cloneElement`, `createContext`, `createRef`, `toChildArray`, `isValidElement`, and `options`. Hooks live in a separate import (`preact/hooks`), and React-API extras live in `preact/compat`. This split is what keeps the base bundle so small.

---

## 2. Installation

### Requirements

| Requirement | Detail |
|---|---|
| Runtime | Any modern browser (Preact targets all modern browsers). A DOM is required for `render`. |
| Module formats | ESM (`import`), CommonJS (`require`), and UMD builds are all published. |
| Build tool | A JSX transform (Babel, esbuild, SWC, TypeScript, or Vite) is recommended but not required — you can also use `h` directly or [HTM](https://github.com/developit/htm) tagged templates with no build step. |
| TypeScript | Type definitions ship with the package (`src/index.d.ts`). No `@types/preact` needed. |

> [!NOTE]
> Preact has **zero runtime dependencies**. Everything in `package.json`'s `devDependencies` (Babel, microbundle, Vitest, oxlint, etc.) is build/test tooling for contributing to Preact itself — none of it is installed into your application.

### Install with your package manager

=== "npm"

    ```bash
    npm install preact
    ```

=== "pnpm"

    ```bash
    pnpm add preact
    ```

=== "yarn"

    ```bash
    yarn add preact
    ```

### The sub-package entry points

Preact ships several independent entry points. Import only what you use — each is tree-shakeable and separately bundled.

| Import path | What it provides |
|---|---|
| `preact` | Core: `render`, `hydrate`, `h`/`createElement`, `Component`, `Fragment`, `createContext`, `createRef`, `cloneElement`, `toChildArray`, `isValidElement`, `options`. |
| `preact/hooks` | All hooks: `useState`, `useReducer`, `useEffect`, `useLayoutEffect`, `useRef`, `useMemo`, `useCallback`, `useContext`, `useImperativeHandle`, `useErrorBoundary`, `useId`, `useDebugValue`. |
| `preact/compat` | React compatibility layer: adds `memo`, `forwardRef`, `PureComponent`, `createPortal`, `Children`, `Suspense`, `lazy`, and React-named hooks/types. Aliasable as `react`/`react-dom`. |
| `preact/jsx-runtime`, `preact/jsx-dev-runtime` | Babel/TypeScript "automatic" JSX runtime (`jsx`, `jsxs`, `jsxDEV`, `Fragment`). |
| `preact/debug` | Development warnings, prop-type checks, and DevTools wiring. Import once at the top of your app. |
| `preact/devtools` | Connects a production build to the Preact DevTools browser extension. |
| `preact/test-utils` | `act`, `setupRerender`, `teardown` for unit tests. |
| `preact/compat/server` | Server-side rendering (`renderToString` and streaming), backed by `preact-render-to-string`. |
| `preact/compat/scheduler`, `preact/compat/client` | React `scheduler` and `react-dom/client` compatibility shims. |

---

## 3. Core Concepts

Before the API reference, here are the ideas that make everything else fall into place.

**Virtual DOM (VNode)**
: A lightweight JavaScript object describing what the UI should look like. You never construct VNodes by hand — JSX (or `h`) creates them. Preact compares (diffs) the new VNode tree against the previous one and updates only what changed.

**Component**
: A function or class that returns a VNode tree. Function components are the default; class components extend `Component` and gain lifecycle methods.

**Props**
: Read-only inputs passed to a component, written as JSX attributes. `children` is a special prop holding nested content.

**State**
: Mutable data owned by a component. Updating state (via the `useState` setter or `this.setState`) schedules a re-render.

**Reconciliation / diffing**
: The process of applying the difference between two VNode trees to the real DOM with as few operations as possible. Preact's diff algorithm is highly optimized and supports keyed lists, Fragments, and SVG/MathML namespaces.

> [!NOTE]
> Preact renders **asynchronously by default**. Calls to `setState` / state setters are batched and flushed on a microtask (via `queueMicrotask`). This is configurable through `options.debounceRendering` — see the Admin Guide for the scheduler internals.

### Preact vs. React: what's different out of the box

| Topic | Plain `preact` | With `preact/compat` |
|---|---|---|
| Attribute name | Use `class` (and `for`) | `className` and `class` both work; `htmlFor`/`for` both work |
| DOM property casing | Standard DOM property names | React-style names are normalized (e.g. `onDoubleClick` → `ondblclick`) |
| `onChange` on text inputs | Fires like the native `change` event (on blur) | Normalized to `oninput` (fires on every keystroke, like React) |
| `defaultProps` | **Not applied** by core | Applied |
| `forwardRef` | Not needed for DOM refs; available via compat | Available |
| `<Suspense>` / `lazy` | Available via `preact/compat` | Available |
| String refs | Not supported | Not supported (use callback or object refs) |

> [!TIP]
> If you're writing a **new** app, prefer plain `preact` + `preact/hooks` and use `class` instead of `className`. Reach for `preact/compat` when you need React-only APIs or want to consume React-ecosystem libraries unchanged.

---

## 4. Rendering: `render` and `hydrate`

### `render(vnode, parent)`

Renders a VNode tree into a DOM container, diffing against any previous tree rendered into the same container.

```jsx
import { render, h } from 'preact';

render(
	<main>
		<h1>Hello</h1>
	</main>,
	document.body
);

// Call render() again on the same container to update in place.
render(
	<main>
		<h1>Hello World!</h1>
	</main>,
	document.body
);
// ^ updates only the <h1> text — a single DOM operation.
```

| Parameter | Type | Description |
|---|---|---|
| `vnode` | `ComponentChild` | The element/component tree to render. |
| `parent` | `ContainerNode` | A DOM element, document fragment, or shadow root to render into. |

> [!NOTE]
> **v11 change:** core `render()` and `hydrate()` take exactly two arguments and return `void`. The optional third `callback` argument and the returned component reference exist only in `preact/compat`'s `render`/`hydrate`. If you relied on `render(vnode, parent, callback)`, import `render` from `preact/compat` or move the callback into an effect.

> [!CAUTION]
> Calling `render()` on `document` itself is redirected to `document.documentElement`. Prefer rendering into a dedicated container element (e.g. `<div id="app">`) you control.

### `hydrate(vnode, parent)`

Attaches Preact to existing server-rendered HTML without re-creating the DOM. Use this on the client when the initial markup was produced by server-side rendering. Hydration attaches event listeners and component state to the existing nodes and skips creating elements that already match.

```jsx
import { hydrate } from 'preact';
import App from './App';

hydrate(<App />, document.getElementById('app'));
```

> [!WARNING]
> The hydrated tree must produce the **same** DOM structure as the server-rendered HTML. A mismatch triggers a hydration error (see [Troubleshooting](#16-troubleshooting)). Avoid rendering values that differ between server and client (e.g. `Date.now()`, random IDs) during the first render.

---

## 5. Creating Elements: `h` / `createElement` and JSX

JSX is compiled into calls to `h` (also exported as `createElement`). These are equivalent:

```jsx
// JSX
const vnode = <a href="/" class="link">Home</a>;

// Compiled output (classic runtime)
import { h } from 'preact';
const vnode = h('a', { href: '/', class: 'link' }, 'Home');
```

### `createElement(type, props, ...children)` (alias `h`)

| Parameter | Type | Description |
|---|---|---|
| `type` | `string \| Component \| Function` | A tag name (`'div'`), a function component, or a class component. |
| `props` | `object \| null` | Attributes/props. `key` and `ref` are extracted specially. |
| `...children` | `ComponentChildren` | Any number of child VNodes, strings, numbers, arrays, or `null`. |

Returns a `VNode`.

> [!NOTE]
> In plain Preact, the `ref` prop is **only** extracted as a ref when `type` is a DOM string. On function/class components, you pass `ref` through `props` and handle it yourself, or use `forwardRef` from `preact/compat`.

### `Fragment`

Group multiple children without adding an extra DOM node.

```jsx
import { Fragment } from 'preact';

function List() {
	return (
		<Fragment>
			<li>One</li>
			<li>Two</li>
		</Fragment>
	);
}
// Or use the shorthand:
function List2() {
	return (
		<>
			<li>One</li>
			<li>Two</li>
		</>
	);
}
```

### Configuring the JSX transform

=== "Automatic runtime (recommended)"

    Babel `@babel/preset-react` or TypeScript `tsconfig.json`:

    ```json
    {
      "compilerOptions": {
        "jsx": "react-jsx",
        "jsxImportSource": "preact"
      }
    }
    ```

    No per-file pragma needed; the compiler imports `jsx`/`jsxs` from `preact/jsx-runtime` automatically.

=== "Classic runtime"

    Add a per-file pragma, or configure it globally:

    ```js
    /** @jsx h */
    import { h } from 'preact';
    ```

    TypeScript equivalent:

    ```json
    {
      "compilerOptions": {
        "jsx": "react",
        "jsxFactory": "h",
        "jsxFragmentFactory": "Fragment"
      }
    }
    ```

=== "No build step (HTM)"

    ```js
    import { h, render } from 'preact';
    import htm from 'htm';

    const html = htm.bind(h);
    render(html`<h1>Hello, no transpiler!</h1>`, document.body);
    ```

### `isValidElement(vnode)`

Returns `true` if the argument is a valid Preact VNode.

```jsx
import { isValidElement, h } from 'preact';

isValidElement(<div />); // true
isValidElement('hello'); // false
```

### `toChildArray(children)`

Flattens the `children` prop into a flat array, dropping `null`/`undefined`/booleans. Useful when you need to count or map over children.

```jsx
import { toChildArray } from 'preact';

function Count(props) {
	return <p>{toChildArray(props.children).length} children</p>;
}
```

### `cloneElement(vnode, props, ...children)`

Returns a copy of a VNode with merged props and (optionally) replaced children. `key` and `ref` from the new props override the originals.

```jsx
import { cloneElement, h } from 'preact';

const original = <button class="primary">Save</button>;
const disabled = cloneElement(original, { disabled: true });
```

---

## 6. Components

### Function components

A function component is any function that returns a VNode tree. It receives `props` as its first argument.

```jsx
function Welcome(props) {
	return <h1>Hello, {props.name}!</h1>;
}

// Usage:
<Welcome name="Ada" />;
```

Function components hold state and side effects through [hooks](#7-hooks-preacthooks).

### Class components

Class components extend `Component` and implement a `render(props, state, context)` method. They gain `this.setState()`, `this.forceUpdate()`, and lifecycle methods.

```jsx
import { Component } from 'preact';

class Clock extends Component {
	state = { time: Date.now() };

	componentDidMount() {
		this.timer = setInterval(() => this.setState({ time: Date.now() }), 1000);
	}

	componentWillUnmount() {
		clearInterval(this.timer);
	}

	render(props, state) {
		return <time>{new Date(state.time).toLocaleTimeString()}</time>;
	}
}
```

> [!TIP]
> Preact passes `props`, `state`, and `context` as arguments to `render()`, so you can destructure them directly: `render({ title }, { open }) { ... }`. You can still use `this.props` / `this.state` if you prefer.

### `this.setState(update, callback?)`

Merges `update` into the component's state and schedules an asynchronous re-render.

| Form | Behavior |
|---|---|
| `this.setState({ key: value })` | Shallow-merges the object into current state. |
| `this.setState((prevState, props) => ({ ... }))` | Functional updater; receives a clone of current state and props. Use this when the next state depends on the previous state. |
| `this.setState({...}, () => {...})` | The callback runs after the component re-renders. |

```jsx
this.setState(prev => ({ count: prev.count + 1 }));
```

> [!NOTE]
> If `setState` is passed a falsy `update` it returns without scheduling a render. State updates are batched — multiple `setState` calls in the same tick result in a single re-render.

### `this.forceUpdate(callback?)`

Forces a synchronous-path re-render, **skipping** `shouldComponentUpdate`. Use sparingly; prefer state-driven updates.

### Lifecycle methods

| Method | When it runs |
|---|---|
| `componentWillMount()` | Before the first render. (Legacy — prefer the constructor or `useEffect`.) |
| `componentDidMount()` | After the component is first rendered into the DOM. |
| `componentWillReceiveProps(nextProps, nextContext)` | Before re-render due to new props. (Legacy.) |
| `shouldComponentUpdate(nextProps, nextState, nextContext)` | Return `false` to skip a re-render. |
| `componentWillUpdate(nextProps, nextState, nextContext)` | Before a re-render (not called on force update). |
| `getSnapshotBeforeUpdate(prevProps, prevState)` | Capture DOM info (e.g. scroll position) right before changes are applied; result is passed to `componentDidUpdate`. |
| `componentDidUpdate(prevProps, prevState, snapshot)` | After a re-render is committed. |
| `componentWillUnmount()` | Right before the component is removed. Clean up timers/subscriptions here. |
| `componentDidCatch(error, errorInfo)` | Catch errors thrown by descendants (error boundary). |
| `static getDerivedStateFromProps(props, state)` | Compute derived state from props before render. |
| `static getDerivedStateFromError(error)` | Update state in response to a descendant error (error boundary). |

> [!IMPORTANT]
> With `preact/compat` installed, the `UNSAFE_`-prefixed aliases (`UNSAFE_componentWillMount`, `UNSAFE_componentWillReceiveProps`, `UNSAFE_componentWillUpdate`) are wired to their unprefixed counterparts for React compatibility. In plain Preact, use the unprefixed names.

### Error boundaries

Catch rendering errors in a subtree and show a fallback:

```jsx
import { Component } from 'preact';

class ErrorBoundary extends Component {
	state = { error: null };

	static getDerivedStateFromError(error) {
		return { error };
	}

	componentDidCatch(error, errorInfo) {
		console.error(error, errorInfo.componentStack);
	}

	render(props, state) {
		return state.error
			? <p>Something went wrong: {String(state.error)}</p>
			: props.children;
	}
}
```

For function components, use the [`useErrorBoundary`](#useerrorboundary) hook instead.

---

## 7. Hooks (`preact/hooks`)

Hooks add state and lifecycle to function components. Import them from `preact/hooks` (not from `preact`).

> [!CAUTION]
> Hooks must be called **unconditionally, in the same order, on every render**, and only from inside a component's render (or another hook). Calling a hook conditionally, in a loop, or outside render throws: `Hook can only be invoked from render methods.`

### `useState(initialState)`

Returns a `[value, setValue]` pair.

```jsx
import { useState } from 'preact/hooks';

const [name, setName] = useState('');
const [count, setCount] = useState(() => expensiveInitial()); // lazy init

setCount(c => c + 1); // functional update
```

The initial value may be a value or a function (called once for lazy initialization). The setter accepts a new value or an updater function. Preact bails out of re-rendering when the new value is `Object.is`-equal to the current one.

### `useReducer(reducer, initialState, init?)`

A `useState` alternative for complex state logic.

```jsx
import { useReducer } from 'preact/hooks';

function reducer(state, action) {
	switch (action.type) {
		case 'inc': return { count: state.count + 1 };
		case 'dec': return { count: state.count - 1 };
		default: return state;
	}
}

const [state, dispatch] = useReducer(reducer, { count: 0 });
dispatch({ type: 'inc' });
```

The optional third argument `init(initialArg)` lazily computes the initial state.

### `useEffect(effect, inputs?)`

Runs a side effect **after** the browser paints. Return a cleanup function to run before the next effect or on unmount. The effect re-runs only when a value in the `inputs` array changes (compared with `Object.is`).

```jsx
import { useEffect } from 'preact/hooks';

useEffect(() => {
	const id = setInterval(tick, 1000);
	return () => clearInterval(id); // cleanup
}, []); // [] = run once on mount
```

| `inputs` | Behavior |
|---|---|
| omitted | Effect runs after every render. |
| `[]` | Effect runs once after mount; cleanup on unmount. |
| `[a, b]` | Effect re-runs whenever `a` or `b` changes. |

### `useLayoutEffect(effect, inputs?)`

Same signature as `useEffect`, but runs **synchronously after DOM mutations and before the browser paints**. Use it to read layout (sizes, scroll) and apply synchronous visual changes without flicker. Prefer `useEffect` when you don't need synchronous timing.

### `useRef(initialValue)`

Returns a mutable `{ current }` object that persists across renders. Mutating `.current` does **not** trigger a re-render. Useful for DOM references and for storing any mutable value.

```jsx
import { useRef } from 'preact/hooks';

function TextInput() {
	const inputRef = useRef(null);
	return <input ref={inputRef} />;
}
```

### `useImperativeHandle(ref, createHandle, inputs?)`

Customize the value exposed to a parent's ref. Typically paired with `forwardRef`.

```jsx
useImperativeHandle(ref, () => ({
	focus: () => inputRef.current.focus()
}), []);
```

### `useMemo(factory, inputs)`

Memoizes the result of `factory()`, recomputing only when `inputs` change.

```jsx
import { useMemo } from 'preact/hooks';

const sorted = useMemo(() => items.slice().sort(compare), [items]);
```

### `useCallback(callback, inputs)`

Returns a memoized version of `callback` that changes only when `inputs` change. Equivalent to `useMemo(() => callback, inputs)`. Useful for stable function identity when passing callbacks to memoized children.

```jsx
const onClick = useCallback(() => doThing(id), [id]);
```

### `useContext(context)`

Reads the current value of a Context (see [Context](#8-context)). Re-renders the component when the nearest provider's value changes.

```jsx
import { useContext } from 'preact/hooks';
const theme = useContext(ThemeContext);
```

### `useErrorBoundary(callback?)`

Function-component error boundary. Returns `[error, resetError]`. The optional `callback(error, errorInfo)` runs when a descendant throws.

```jsx
import { useErrorBoundary } from 'preact/hooks';

function App() {
	const [error, resetError] = useErrorBoundary();
	if (error) {
		return <button onClick={resetError}>Try again</button>;
	}
	return <Child />;
}
```

### `useId()`

Returns a stable, unique string ID that is consistent between server and client renders — ideal for linking form labels and inputs accessibly.

```jsx
import { useId } from 'preact/hooks';

function Field() {
	const id = useId();
	return (
		<>
			<label for={id}>Email</label>
			<input id={id} type="email" />
		</>
	);
}
```

### `useDebugValue(value, formatter?)`

Displays a label for a custom hook in the Preact DevTools. No-op when DevTools is not attached.

```jsx
useDebugValue(isOnline ? 'Online' : 'Offline');
```

> [!TIP]
> Hook execution timing at a glance: `useLayoutEffect` cleanup/run happens synchronously during commit; `useEffect` runs after paint (scheduled via `requestAnimationFrame` + a 35 ms `setTimeout` fallback so effects still fire in background tabs).

---

## 8. Context

Context passes data through the component tree without threading props at every level.

### `createContext(defaultValue)`

```jsx
import { createContext } from 'preact/hooks';
import { createContext as createCtx } from 'preact';

const ThemeContext = createCtx('light');
```

Provide a value with the context itself (or its `.Provider`), and consume it with `useContext` or `<Context.Consumer>`:

```jsx
import { createContext } from 'preact';
import { useContext } from 'preact/hooks';

const ThemeContext = createContext('light');

function App() {
	return (
		<ThemeContext value="dark">
			<Toolbar />
		</ThemeContext>
	);
}

function Toolbar() {
	const theme = useContext(ThemeContext);
	return <div class={theme}>Current theme: {theme}</div>;
}
```

> [!NOTE]
> **v11:** a context object is itself a provider — you can render `<ThemeContext value={...}>` directly. The classic `<ThemeContext.Provider>` and `<ThemeContext.Consumer>` forms remain available for compatibility. When no provider is present in the tree, `useContext` returns the `defaultValue` you passed to `createContext`.

The render-prop Consumer form:

```jsx
<ThemeContext.Consumer>
	{value => <span>{value}</span>}
</ThemeContext.Consumer>
```

---

## 9. Refs

A ref gives you direct access to a DOM node or class-component instance.

### `createRef()`

Creates a `{ current: null }` ref object (for class components).

```jsx
import { Component, createRef } from 'preact';

class Form extends Component {
	input = createRef();
	componentDidMount() {
		this.input.current.focus();
	}
	render() {
		return <input ref={this.input} />;
	}
}
```

### Callback refs

Pass a function to `ref`; it receives the node (or `null` on unmount):

```jsx
<input ref={node => (this.input = node)} />
```

### Ref hook (function components)

Use [`useRef`](#userefinitialvalue) in function components. For DOM refs, no `forwardRef` is needed when forwarding to a DOM element directly — but to forward a ref *through* a function component to a child, use `forwardRef` from `preact/compat`.

> [!WARNING]
> A `ref` must be a function or an object created by `createRef()`/`useRef()`. Passing any other value throws in development: `Component's "ref" property should be a function, or an object created by createRef(), but got [type] instead`.

---

## 10. The React Compatibility Layer (`preact/compat`)

`preact/compat` makes Preact behave like React, so you can run React components and consume React-ecosystem libraries. It re-exports everything from `preact` and `preact/hooks`, plus the React-only APIs below. It reports `version` as `"18.3.1"` so libraries that sniff the React version behave correctly.

### Using compat as a React drop-in

Alias `react` and `react-dom` to `preact/compat` in your bundler so existing React code resolves to Preact.

=== "Vite / Rollup (`vite.config.js`)"

    ```js
    export default {
      resolve: {
        alias: {
          react: 'preact/compat',
          'react-dom': 'preact/compat',
          'react/jsx-runtime': 'preact/jsx-runtime',
          'react-dom/test-utils': 'preact/test-utils'
        }
      }
    };
    ```

=== "Webpack (`webpack.config.js`)"

    ```js
    module.exports = {
      resolve: {
        alias: {
          react: 'preact/compat',
          'react-dom': 'preact/compat',
          'react/jsx-runtime': 'preact/jsx-runtime'
        }
      }
    };
    ```

=== "TypeScript (`tsconfig.json`)"

    ```json
    {
      "compilerOptions": {
        "paths": {
          "react": ["./node_modules/preact/compat/"],
          "react-dom": ["./node_modules/preact/compat/"]
        }
      }
    }
    ```

### What compat adds

| API | Purpose |
|---|---|
| `memo(Component, areEqual?)` | Skip re-render when props are shallow-equal (or per a custom comparer). |
| `forwardRef(fn)` | Forward a ref through a function component to a child. |
| `PureComponent` | Class component with a built-in shallow-compare `shouldComponentUpdate`. |
| `createPortal(vnode, container)` | Render children into a different DOM node. |
| `Children` | `map`, `forEach`, `count`, `only`, `toArray` helpers over `props.children`. |
| `Suspense`, `lazy` | Code-splitting and async boundaries (see next section). |
| `StrictMode` | Alias of `Fragment` (a no-op wrapper). |
| `createFactory(type)` | Legacy element factory. |
| `isValidElement`, `isFragment`, `isMemo`, `isElement` | Element type guards. |
| `findDOMNode(component)` | Get the DOM node for a component (legacy). |
| `unmountComponentAtNode(container)` | Tear down a tree (legacy). |
| `flushSync(fn, arg?)` | Run `fn` and flush all pending updates synchronously. |
| `unstable_batchedUpdates(fn, arg?)` | Legacy batching shim (Preact already batches; this just calls `fn`). |
| `startTransition`, `useTransition`, `useDeferredValue` | React 18 concurrency shims (synchronous in Preact). |
| `useSyncExternalStore` | Subscribe to an external store (React 18 API). |
| `useInsertionEffect` | Aliased to `useLayoutEffect`. |

> [!NOTE]
> **DOM prop normalization in compat.** When compat is active, vnodes are normalized to React conventions: `className` ⇄ `class`; `onChange` on text `<input>`/`<textarea>` becomes `oninput` (fires on every keystroke); `onDoubleClick` → `ondblclick`; `onFocus`/`onBlur` → `onfocusin`/`onfocusout`; camelCased SVG attributes are dashed; numeric `style` values get a `px` suffix where applicable; `defaultValue`/`defaultChecked` are honored; `download={true}` renders as `download=""`; and array `value` on `<select multiple>` is supported. `defaultProps` on components are applied. Events also gain `persist()`, `nativeEvent`, `isPropagationStopped()`, and `isDefaultPrevented()`.

### `memo`

```jsx
import { memo } from 'preact/compat';

const Row = memo(function Row({ label }) {
	return <li>{label}</li>;
});

// Custom comparison:
const Row2 = memo(Row, (prev, next) => prev.id === next.id);
```

### `forwardRef`

```jsx
import { forwardRef } from 'preact/compat';

const FancyInput = forwardRef(function FancyInput(props, ref) {
	return <input ref={ref} class="fancy" {...props} />;
});
```

### `createPortal`

```jsx
import { createPortal } from 'preact/compat';

function Modal({ children }) {
	return createPortal(
		<div class="modal">{children}</div>,
		document.getElementById('modal-root')
	);
}
```

---

## 11. Suspense and Lazy Loading

`Suspense` and `lazy` (from `preact/compat`) enable code-splitting and async boundaries. `lazy` wraps a dynamic `import()`; `Suspense` shows a `fallback` while the lazy component (or any descendant that "suspends" by throwing a promise) loads.

```jsx
import { Suspense, lazy } from 'preact/compat';

const Profile = lazy(() => import('./Profile'));

function App() {
	return (
		<Suspense fallback={<div>Loading…</div>}>
			<Profile />
		</Suspense>
	);
}
```

| API | Signature | Notes |
|---|---|---|
| `lazy(loader)` | `loader: () => Promise<{ default: C } \| C>` | Returns a component that throws the load promise until resolved, then renders the default export. |
| `Suspense` | `props: { fallback, children }` | Renders `fallback` while any child is suspended, then swaps in the children. |

> [!WARNING]
> A component that suspends (throws a promise) **must** have a `<Suspense>` ancestor. Without one, you get: `Missing Suspense. The throwing component was: <Name>`. Suspense boundaries can be nested to provide granular loading states.

> [!TIP]
> Suspense also works with hydration: server-rendered markup inside a `<Suspense>` is kept on screen and hydrated when the lazy content resolves, rather than being replaced by the fallback.

---

## 12. Server-Side Rendering

Preact renders to an HTML string on the server with the separate [`preact-render-to-string`](https://github.com/preactjs/preact-render-to-string) package, surfaced through `preact/compat/server`.

```bash
npm install preact-render-to-string
```

```jsx
import { renderToString } from 'preact/compat/server';
import App from './App';

const html = renderToString(<App />);
// Send `html` to the browser, then call hydrate() on the client.
```

| Export (`preact/compat/server`) | Purpose |
|---|---|
| `renderToString(vnode)` | Render a tree to a complete HTML string. |
| `renderToStaticMarkup(vnode)` | Alias of `renderToString` (no extra hydration attributes). |
| `renderToPipeableStream(vnode)` | Node.js streaming SSR. |
| `renderToReadableStream(vnode)` | Web-streams SSR. |

> [!CAUTION]
> The streaming functions (`renderToPipeableStream`, `renderToReadableStream`) require `preact-render-to-string` **≥ 6.5.0**. If the dependency is missing or too old, you'll see an error like `renderToReadableStream() error: update "preact-render-to-string" dependency to at least 6.5.0.`

On the client, finish by hydrating the server markup:

```jsx
import { hydrate } from 'preact';
hydrate(<App />, document.getElementById('app'));
```

---

## 13. Debugging (`preact/debug`) and DevTools

### `preact/debug`

Import `preact/debug` **once, before any other Preact import**, in development builds. It installs runtime validation that surfaces common mistakes as clear console errors and warnings, and connects to the DevTools extension.

```jsx
// At the very top of your app entry (development only):
import 'preact/debug';
```

What it checks (a selection):

- Undefined/invalid components passed to `createElement`.
- Invalid `ref` and event-handler prop types.
- Rendering objects as children.
- Duplicate `key`s among siblings.
- Improper HTML nesting (e.g. `<td>` outside `<tr>`, block elements inside `<p>`, nested `<a>`/`<button>`).
- Hooks called outside render; too many re-renders (infinite loop guard at 25).
- `NaN` in a hook dependency array.
- `setState`/`forceUpdate` called in a constructor or on an unmounted component.
- Hydration mismatches between SSR and client markup.

> [!IMPORTANT]
> Never ship `preact/debug` to production — it adds size and runtime overhead. Strip it via your bundler's production conditionals (e.g. only import it when `process.env.NODE_ENV !== 'production'`).

`preact/debug` also re-exports stack helpers: `captureOwnerStack()`, `getCurrentVNode()`, `getDisplayName(vnode)`, `getOwnerStack(vnode)`, `setupComponentStack()`, and `resetPropWarnings()`.

### `preact/devtools`

To use the [Preact DevTools](https://preactjs.github.io/preact-devtools/) browser extension in a **production** build (where `preact/debug` is absent), import `preact/devtools`:

```jsx
import 'preact/devtools';
```

It also exports `addHookName(value, name)` to label custom hooks in the DevTools panel.

---

## 14. Testing (`preact/test-utils`)

`preact/test-utils` helps you write deterministic tests by flushing Preact's async renders and effects.

| Function | Purpose |
|---|---|
| `act(callback)` | Run `callback`, then flush all pending re-renders and effects. Returns a Promise. Supports nesting and async callbacks. |
| `setupRerender()` | Returns a function that synchronously drains the pending render queue. |
| `teardown()` | Reset Preact's internal scheduling state between tests. |

```jsx
import { render } from 'preact';
import { act } from 'preact/test-utils';

await act(() => {
	render(<Counter />, scratch);
});
// State updates and effects have now flushed; assert on the DOM.
```

> [!TIP]
> Wrap any code that triggers state updates in `act()` so effects and re-renders complete before your assertions run.

---

## 15. Common Tasks (Recipes)

### Conditional rendering

```jsx
function Greeting({ user }) {
	return user ? <p>Welcome, {user.name}</p> : <p>Please sign in</p>;
}
```

### Rendering lists with keys

```jsx
function TodoList({ items }) {
	return (
		<ul>
			{items.map(item => (
				<li key={item.id}>{item.text}</li>
			))}
		</ul>
	);
}
```

> [!WARNING]
> Always give list items a stable, unique `key`. Duplicate keys among siblings trigger a console error and can cause incorrect DOM reuse during reordering.

### Controlled form input

```jsx
import { useState } from 'preact/hooks';

function NameForm() {
	const [name, setName] = useState('');
	return (
		<input value={name} onInput={e => setName(e.currentTarget.value)} />
	);
}
```

> [!NOTE]
> In plain Preact use `onInput` for keystroke-level updates. With `preact/compat`, React's `onChange` on text inputs is automatically mapped to `onInput`.

### Inline styles

```jsx
<div style={{ color: 'red', marginTop: 8 }}>Styled</div>
```

In `preact/compat`, numeric values for dimensional properties (e.g. `marginTop: 8`) are suffixed with `px` automatically.

### Lazy-loaded route

```jsx
import { Suspense, lazy } from 'preact/compat';
const Settings = lazy(() => import('./routes/Settings'));

<Suspense fallback={<Spinner />}>
	<Settings />
</Suspense>;
```

### Custom hook

```jsx
import { useState, useEffect } from 'preact/hooks';

function useOnline() {
	const [online, setOnline] = useState(navigator.onLine);
	useEffect(() => {
		const on = () => setOnline(true);
		const off = () => setOnline(false);
		addEventListener('online', on);
		addEventListener('offline', off);
		return () => {
			removeEventListener('online', on);
			removeEventListener('offline', off);
		};
	}, []);
	return online;
}
```

---

## 16. Troubleshooting

These are real validation messages emitted by `preact/debug`, plus common runtime issues. Each entry lists the cause, fix, and how to prevent it.

### Error: `Hook can only be invoked from render methods.`

- **Cause:** A hook (`useState`, `useEffect`, etc.) was called outside a component's render — e.g. in an event handler, at module top level, conditionally, or in a loop.
- **Solution:** Move the hook to the top level of a function component or custom hook. Call hooks unconditionally and in the same order every render.
- **Prevention:** Follow the Rules of Hooks; use an ESLint hooks plugin.

### Error: `Undefined component passed to createElement()`

- **Cause:** A component used in JSX is `undefined` — usually a wrong import (mixing default and named imports) or a forgotten `export`.
- **Solution:** Verify the import/export. `import Foo from './Foo'` requires `export default`; `import { Foo }` requires `export function Foo`.
- **Prevention:** Keep import style consistent with the export; let TypeScript catch missing exports.

### Error: `Invalid type passed to createElement(): ... Did you accidentally pass a JSX literal as JSX twice?`

- **Cause:** A VNode (the result of evaluating JSX) was used as a component type, e.g. `export default <App />` then `<MyApp />`.
- **Solution:** Export the component itself (`export default App`), not a rendered element.
- **Prevention:** Don't store JSX literals where a component is expected.

### Error: `Component's "ref" property should be a function, or an object created by createRef(), but got [<type>] instead`

- **Cause:** A `ref` was passed a value that isn't a function or `createRef()`/`useRef()` object.
- **Solution:** Use a callback ref or a ref object. String refs are not supported.
- **Prevention:** Create refs with `useRef()`/`createRef()`.

### Error: `Component's "<onX>" property should be a function, but got [<type>] instead`

- **Cause:** An event handler prop (e.g. `onClick`) was given a non-function value.
- **Solution:** Pass a function: `onClick={() => handle()}`.
- **Prevention:** Avoid calling the handler inline (`onClick={handle()}` calls it immediately and assigns the result).

### Error: `Too many re-renders. This is limited to prevent an infinite loop which may lock up your browser. The component causing this is: <Name>`

- **Cause:** A component updates its own state during render (≥ 25 renders in a row for the same component), creating a loop.
- **Solution:** Move state updates into event handlers or effects, not the render body.
- **Prevention:** Never call a state setter unconditionally during render.

### Error: `Objects are not valid as a child. Encountered an object with the keys {<keys>}.`

- **Cause:** A plain object was rendered as a child, e.g. `<div>{someObject}</div>`.
- **Solution:** Render a string/number, or pick specific fields (`{obj.name}`), or `JSON.stringify(obj)`.
- **Prevention:** Only render primitives, VNodes, or arrays of them.

### Error: `Missing Suspense. The throwing component was: <Name>`

- **Cause:** A `lazy` component or other suspending component has no `<Suspense>` ancestor.
- **Solution:** Wrap it in `<Suspense fallback={…}>`.
- **Prevention:** Always pair `lazy()`/suspense-based data loading with a boundary.

### Error: `Undefined parent passed to render(), this is the second argument.`

- **Cause:** The container passed to `render()`/`hydrate()` is `null`/`undefined` — usually `document.getElementById('app')` returned nothing.
- **Solution:** Ensure the container exists in the DOM and the id matches; run after the DOM is ready.
- **Prevention:** Render into a known container; guard against a missing element.

### Error: `Expected a valid HTML node as a second argument to render. Received <x> instead`

- **Cause:** The second argument to `render` isn't an Element, Document, or DocumentFragment.
- **Solution:** Pass a real DOM container node.
- **Prevention:** Don't pass strings, components, or arbitrary objects as the container.

### Warning: `Following component has two or more children with the same key attribute: "<key>"`

- **Cause:** Sibling list items share a `key`.
- **Solution:** Make keys unique within the list (use a stable id, not the array index when items reorder).
- **Prevention:** Derive keys from unique data.

### Error: `Expected a DOM node of type "<type>" but found "<other>" ... caused by the SSR'd HTML containing different DOM-nodes compared to the hydrated one.`

- **Cause:** Hydration mismatch — the client tree differs from the server markup.
- **Solution:** Ensure server and client render identically; avoid non-deterministic values (`Date.now()`, `Math.random()`) during the first render; move them into effects.
- **Prevention:** Render the same output on both sides; use `useId()` for IDs.

### Warning: `Calling "this.setState" inside the constructor of a component is a no-op...`

- **Cause:** `setState` was called in the constructor.
- **Solution:** Set `this.state = { ... }` directly in the constructor instead.
- **Prevention:** Initialize state with a class field or in the constructor by assignment.

### Warning: `Can't call "this.forceUpdate" on an unmounted component...`

- **Cause:** `forceUpdate`/`setState` ran after the component unmounted — often an async callback or subscription that wasn't cleaned up.
- **Solution:** Cancel timers, subscriptions, and pending promises in `componentWillUnmount` (or the `useEffect` cleanup).
- **Prevention:** Always return a cleanup function from effects.

### Warning: `Invalid argument passed to hook. Hooks should not be called with NaN in the dependency array.`

- **Cause:** A `NaN` value appeared in a hook's `inputs` array; `NaN !== NaN`, so the effect would run every render.
- **Solution:** Ensure dependency values are well-defined numbers; guard computations that can produce `NaN`.
- **Prevention:** Validate numeric dependencies.

### Warning: `Add @babel/plugin-transform-react-jsx-source to get a more detailed component stack.`

- **Cause:** Component stacks lack file/line info because the JSX source plugin isn't in your dev toolchain.
- **Solution:** Add `@babel/plugin-transform-react-jsx-source` to development builds only.
- **Prevention:** Keep it out of production builds (it increases bundle size).

---

## 17. FAQ

**Q: Is Preact really compatible with React?**
A: The core API (components, hooks, JSX, context, refs) matches React's. For React-only APIs (`memo`, `forwardRef`, `Suspense`, portals, etc.) and ecosystem libraries, alias `react`/`react-dom` to `preact/compat`.

**Q: Do I have to use JSX?**
A: No. You can call `h`/`createElement` directly, or use [HTM](https://github.com/developit/htm) tagged templates for a build-free workflow.

**Q: Should I use `class` or `className`?**
A: In plain Preact, use `class`. With `preact/compat`, both work and are normalized.

**Q: Why doesn't my text input update on every keystroke with `onChange`?**
A: Plain Preact's `onChange` mirrors the native `change` event (fires on blur). Use `onInput` for per-keystroke updates, or use `preact/compat`, which maps `onChange` to `oninput` for text inputs.

**Q: Where do hooks come from?**
A: Import them from `preact/hooks`, not `preact`. This keeps the core bundle small.

**Q: How big is Preact?**
A: The core is roughly **4 kB** gzipped — see the live badges in the repository README. `preact/hooks` and `preact/compat` add small amounts on top, and everything is tree-shakeable.

**Q: Does Preact have runtime dependencies?**
A: No. The core has zero runtime dependencies. SSR uses the separate `preact-render-to-string` package.

**Q: Are `setState` updates synchronous?**
A: No — they're batched and flushed asynchronously on a microtask. Use the `setState` callback, `useEffect`, or `flushSync` (compat) if you need to act after the DOM updates.

**Q: How do I render into a different part of the DOM (e.g. a modal)?**
A: Use `createPortal` from `preact/compat`.

**Q: How do I get a ref to a child function component's DOM node?**
A: Wrap the child with `forwardRef` from `preact/compat`, or expose an imperative API with `useImperativeHandle`.

**Q: What's the difference between `useEffect` and `useLayoutEffect`?**
A: `useEffect` runs after paint (non-blocking). `useLayoutEffect` runs synchronously after DOM mutations but before paint — use it to measure layout or prevent visual flicker.

**Q: Can I use Preact with TypeScript?**
A: Yes — types ship with the package. Set `jsxImportSource: "preact"` (automatic runtime) or `jsxFactory: "h"` (classic runtime).

**Q: How do I enable the DevTools extension?**
A: Import `preact/debug` in development, or `preact/devtools` in production builds.

**Q: Should I use the v11 beta or v10?**
A: For production, use stable v10 (`preact@^10`). Use v11 beta only to test the upcoming release or for greenfield projects that can tolerate pre-release churn.

---

## 18. API Quick Reference

### `preact` (core)

| Export | Signature | Description |
|---|---|---|
| `render` | `render(vnode, parent)` | Render/update a tree into a DOM container. |
| `hydrate` | `hydrate(vnode, parent)` | Attach to existing SSR markup. |
| `h` / `createElement` | `(type, props, ...children) → VNode` | Create a virtual node. |
| `Fragment` | component | Group children without a wrapper node. |
| `Component` | class | Base class for class components. |
| `cloneElement` | `(vnode, props, ...children) → VNode` | Copy a VNode with merged props. |
| `createContext` | `(defaultValue) → Context` | Create a context. |
| `createRef` | `() → { current: null }` | Create a ref object. |
| `toChildArray` | `(children) → array` | Flatten children. |
| `isValidElement` | `(value) → boolean` | VNode type guard. |
| `options` | object | Renderer hook registry (advanced — see Admin Guide). |

### `preact/hooks`

| Hook | Signature |
|---|---|
| `useState` | `(initial) → [value, setValue]` |
| `useReducer` | `(reducer, initial, init?) → [state, dispatch]` |
| `useEffect` | `(effect, inputs?) → void` |
| `useLayoutEffect` | `(effect, inputs?) → void` |
| `useRef` | `(initial) → { current }` |
| `useImperativeHandle` | `(ref, createHandle, inputs?) → void` |
| `useMemo` | `(factory, inputs) → value` |
| `useCallback` | `(callback, inputs) → callback` |
| `useContext` | `(context) → value` |
| `useErrorBoundary` | `(callback?) → [error, reset]` |
| `useId` | `() → string` |
| `useDebugValue` | `(value, formatter?) → void` |

### `preact/compat` (selected)

| Export | Signature |
|---|---|
| `memo` | `(Component, areEqual?) → Component` |
| `forwardRef` | `(fn) → Component` |
| `PureComponent` | class |
| `createPortal` | `(vnode, container) → VNode` |
| `Suspense` | component (`{ fallback, children }`) |
| `lazy` | `(loader) → Component` |
| `Children` | `{ map, forEach, count, only, toArray }` |
| `flushSync` | `(fn, arg?) → result` |
| `startTransition` / `useTransition` / `useDeferredValue` | React 18 concurrency shims |
| `useSyncExternalStore` | `(subscribe, getSnapshot) → value` |

---

_Generated June 2026 from preactjs/preact at 850bbdb. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
