# preactjs/preact — codebase unpack

> Canonical page: https://codevetter.com/unpack/preact

This is a deterministic Repo Unpack scan of preactjs/preact — a structural map, not a review, security audit, or maintainer-endorsed description.

- Scanned commit: 45728eb4315bcb22c5d50b8fec0bbac419eaa799 (2026-10-02)
- Last upstream commit: Merge pull request #5292 from piotr-cz/patch-1
- Generated by codevetter 1.15.0 on 2026-10-02
- 302 files scanned · 1.5 MB

Source inspection; upstream code and tests were not executed.

## Languages

- JavaScript: 202 files
- TypeScript: 41 files
- JSON: 22 files
- YAML: 9 files
- Sass: 7 files
- Markdown: 6 files
- CSS: 1 files
- HTML: 1 files

## Repository structure

- compat/ — 79 files
- test/ — 57 files
- demo/ — 52 files
- hooks/ — 23 files
- debug/ — 22 files
- src/ — 17 files
- .github/ — 12 files
- jsx-runtime/ — 7 files

Probable source entrypoint: `src/index.js` (inferred from the tree, not upstream docs)

## Source layout — src/

- src/diff/ — 4 files
- src/component.js — 1 file
- src/constants.js — 1 file
- src/create-context.js — 1 file
- src/create-element.js — 1 file
- src/create-portal.js — 1 file
- src/dom.d.ts — 1 file
- src/index.d.ts — 1 file
- src/index.js — 1 file
- src/internal.d.ts — 1 file
- src/jsx.d.ts — 1 file
- src/options.js — 1 file
- src/render.js — 1 file
- src/util.js — 1 file

## Package

- package.json: preact v11.0.0 — 24 declared dependencies

## Structural health signals

Average score 7.4/10 across 96 files; 14 hotspots. These are deterministic review leads, not proof of a bug.

## Recent history

- 45728eb4315b (2026-10-02) Merge pull request #5292 from piotr-cz/patch-1
- 077c00a72132 (2026-10-02) Change repository field to object format
- 3fcc391adc24 (2026-09-30) Merge pull request #5282 from preactjs/JoviDeCroock/compat-server-types
- ccd48784806a (2026-09-30) Merge branch 'main' into JoviDeCroock/compat-server-types
- e6ec2da5673e (2026-09-30) Merge pull request #5266 from preactjs/11.0.0

## Overview

Preact is a component-based UI library for developers building browser interfaces with JSX, functions, classes, hooks, and context. At commit 45728eb4315bcb22c5d50b8fec0bbac419eaa799, its renderer reconciles virtual nodes against live DOM, supports hydration and portals, and exposes renderer callbacks that power separately bundled addons. React compatibility, development diagnostics, JSX compiler helpers, and testing utilities surround the core; server rendering delegates to the separate preact-render-to-string package.

## System map

The repository packages a DOM renderer and several addons under one public npm package. Core rendering owns VNode trees, component instances, and DOM mutations; hooks and compatibility extend it through shared options callbacks. Server rendering crosses into an optional external package, while the demo is a separate Vite application inside the workspace. Rollup, Babel, and Terser produce the distributable modules, and GitHub Actions define validation and staged release workflows.
- src/index.js exports render, hydrate, createElement/h, Fragment, refs, cloning, Component, context, portals, child flattening, and options as the core API. — [src/index.js#L1-14](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/index.js#L1-L14)
- The root package is preact 11.0.0; its export map publishes core, compat, debug, devtools, hooks, test-utils, JSX runtimes, and compatibility client/server/scheduler adapters. — [package.json#L1-17](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/package.json#L1-L17) [package.json#L19-87](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/package.json#L19-L87)
- compat and debug manifests are private source/build metadata with separate dist modules; CONTRIBUTING describes addon manifests as publishing metadata rather than workspace projects. — [compat/package.json#L1-15](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/package.json#L1-L15) [debug/package.json#L1-15](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/debug/package.json#L1-L15) [CONTRIBUTING.md#L14-16](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/CONTRIBUTING.md#L14-L16)
- The pnpm workspace adds only demo; package.json selects pnpm 11.3.0 and Node 22.22.2, and workspace configuration enforces that Node engine. — [pnpm-workspace.yaml#L1-4](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/pnpm-workspace.yaml#L1-L4) [pnpm-workspace.yaml#L23-25](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/pnpm-workspace.yaml#L23-L25) [package.json#L215-218](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/package.json#L215-L218)
- render stores the previous root on parentDom._children, while Component owns state and _nextState; these inspected core paths retain runtime state in objects associated with the DOM tree. — [src/render.js#L24-31](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/render.js#L24-L31) [src/component.js#L35-59](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/component.js#L35-L59)
- scripts/build.mjs enumerates seven bundles and uses Rollup, Babel property renaming, a custom rest transform, and Terser; addons externalize preact imports. — [scripts/build.mjs#L23-31](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/scripts/build.mjs#L23-L31) [scripts/build.mjs#L82-106](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/scripts/build.mjs#L82-L106) [scripts/build.mjs#L137-172](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/scripts/build.mjs#L137-L172)
- Server compatibility exports string, pipeable-stream, and readable-stream rendering from preact-render-to-string; the root declares it an optional peer. — [compat/server.mjs#L1-17](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/server.mjs#L1-L17) [package.json#L207-213](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/package.json#L207-L213) [CONTRIBUTING.md#L75-75](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/CONTRIBUTING.md#L75-L75)
- The demo uses Vite with automatic JSX and source aliases for core, hooks, devtools, JSX runtimes, React, and ReactDOM. — [demo/package.json#L4-14](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/package.json#L4-L14) [demo/vite.config.js#L24-46](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/vite.config.js#L24-L46)
- Repository scripts define build, addon-specific watch modes, lint, formatting, minified Vitest tests, and separate core/compat TypeScript checks. — [package.json#L95-115](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/package.json#L95-L115)
- Release automation responds to 11.* tags, consumes the tested npm-package artifact, creates a draft GitHub release, and stages npm publication with provenance; CONTRIBUTING describes a subsequent npm review and 2FA step. — [.github/workflows/release.yml#L3-20](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/release.yml#L3-L20) [.github/workflows/release.yml#L52-76](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/release.yml#L52-L76) [.github/workflows/release.yml#L127-128](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/release.yml#L127-L128) [CONTRIBUTING.md#L209-216](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/CONTRIBUTING.md#L209-L216)

## Feature catalog

The primary user-facing surface is a library API for constructing and updating interfaces. Core provides components, DOM rendering, hydration, context, fragments, refs, and portals; addons provide hooks, React adapters, diagnostics, and test controls. Compiler-facing exports support both automatic JSX and precompiled HTML templates. The demo exposes a route-based playground for rendering scenarios and ecosystem integrations; it is documented as a debugging application.
- createElement and cloneElement normalize props and variadic children, extract keys, and extract refs for non-function element types; Fragment returns its children. — [src/create-element.js#L16-33](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/create-element.js#L16-L33) [src/create-element.js#L44-68](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/create-element.js#L44-L68) [src/create-element.js#L108-114](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/create-element.js#L108-L114)
- Class components support setState, forceUpdate, derived state, mount/update callbacks, shouldComponentUpdate, and snapshot-before-update behavior. — [src/component.js#L35-77](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/component.js#L35-L77) [src/diff/index.js#L163-175](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L163-L175) [src/diff/index.js#L183-256](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L183-L256) [src/diff/index.js#L296-302](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L296-L302)
- Hydration reuses matching existing DOM nodes, attaches function-valued props, reports missing-node mismatches through an option hook, and specially synchronizes textarea values. — [src/render.js#L69-77](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/render.js#L69-L77) [src/diff/index.js#L549-583](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L549-L583) [src/diff/index.js#L634-652](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L634-L652) [src/diff/index.js#L697-716](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L697-L716)
- createContext provides Provider and Consumer interfaces, default values, and subscriptions that force consumer updates when the provider value changes. — [src/create-context.js#L6-49](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/create-context.js#L6-L49)
- createPortal continues a VNode subtree in another DOM container; compat adds containerInfo to the returned node. — [src/create-portal.js#L9-19](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/create-portal.js#L9-L19) [compat/src/portals.js#L8-11](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/portals.js#L8-L11)
- preact/hooks implements useState/useReducer, passive and layout effects, refs, imperative handles, memoization, callbacks, context, debug values, error boundaries, and stable per-root IDs. — [hooks/src/index.js#L200-229](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L200-L229) [hooks/src/index.js#L281-338](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L281-L338) [hooks/src/index.js#L347-399](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L347-L399) [hooks/src/index.js#L407-442](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L407-L442)
- React compatibility includes Children utilities, PureComponent, memo with a custom comparator, forwardRef, DOM lookup, unmounting, flushSync, and StrictMode as a Fragment alias. — [compat/src/index.js#L104-123](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/index.js#L104-L123) [compat/src/index.js#L134-151](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/index.js#L134-L151) [compat/src/index.js#L166-202](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/index.js#L166-L202) [compat/src/memo.js#L11-30](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/memo.js#L11-L30)
- Compat supplies Suspense and lazy, use for promises or context, and useSyncExternalStore with separate server/hydration snapshots. — [compat/src/suspense.js#L136-200](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/suspense.js#L136-L200) [compat/src/suspense.js#L241-276](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/suspense.js#L241-L276) [compat/src/render.js#L39-76](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/render.js#L39-L76) [compat/src/render.js#L346-385](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/render.js#L346-L385)
- Compat client adapters expose createRoot with render/unmount and hydrateRoot; server adapters expose string/static-markup and stream interfaces. — [compat/client.mjs#L3-24](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/client.mjs#L3-L24) [compat/server.mjs#L5-17](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/server.mjs#L5-L17) [compat/server.browser.js#L1-11](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/server.browser.js#L1-L11)
- The JSX runtime exports jsx, jsxs, jsxDEV, Fragment, jsxTemplate, jsxAttr, and jsxEscape; template helpers serialize attributes and escape dynamic children. — [jsx-runtime/src/index.js#L81-86](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/jsx-runtime/src/index.js#L81-L86) [jsx-runtime/src/index.js#L113-149](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/jsx-runtime/src/index.js#L113-L149) [jsx-runtime/src/index.js#L158-191](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/jsx-runtime/src/index.js#L158-L191)
- Debug validates render containers, element types, refs, event-handler values, hook placement, excessive rerenders, and duplicate keys; devtools exposes an attachment bridge and custom hook names. — [debug/src/debug.js#L121-207](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/debug/src/debug.js#L121-L207) [debug/src/debug.js#L249-282](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/debug/src/debug.js#L249-L282) [debug/src/debug.js#L448-470](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/debug/src/debug.js#L448-L470) [devtools/src/index.js#L1-15](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/devtools/src/index.js#L1-L15)
- The demo route table exposes Home, state-order, reorder, spiral, pythagoras, todo, fragments, profiler, key_bug, context, devtools, suspense, empty-fragment, people profiles, styled-components, Redux, MobX, textfields, reduxBug, suspense-router, nested-suspense, contenteditable, Zustand, and Redux Toolkit. — [demo/index.jsx#L149-178](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/index.jsx#L149-L178) [CONTRIBUTING.md#L67-69](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/CONTRIBUTING.md#L67-L69)
- The Todo demo supports text entry, addition, and removal; People Browser supports name/ID sorting, profile navigation, and delayed contact removal using an in-memory MobX store. — [demo/todo.jsx#L5-45](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/todo.jsx#L5-L45) [demo/people/index.tsx#L19-53](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/people/index.tsx#L19-L53) [demo/people/store.ts#L60-73](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/people/store.ts#L60-L73) [demo/people/profile.tsx#L49-58](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/people/profile.tsx#L49-L58)

## Data flow

Applications supply component types, props, children, and a DOM container; factories turn these into renderer-owned VNodes. Reconciliation links new nodes to previous nodes, updates component state, and applies DOM changes before committing refs and callbacks. Hook state belongs to component instances, with separate queues for pending values and effects. External stores, promises, and demo network responses enter through explicit adapters; the inspected core state path does not itself implement durable application persistence.
- createElement converts a type/props/children call into createVNode; the factory initializes tree pointers, component ownership, identity, flags, and an undefined constructor marker before invoking options.vnode. — [src/create-element.js#L16-33](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/create-element.js#L16-L33) [src/create-element.js#L82-105](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/create-element.js#L82-L105)
- Automatic JSX uses a separate VNode factory with the same internal fields, negative identity counters, optional source metadata, and the shared options.vnode callback. — [jsx-runtime/src/index.js#L28-71](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/jsx-runtime/src/index.js#L28-L71)
- render wraps application output in a Fragment root, passes previous VNodes or existing childNodes into diff, and collects ref and component callback queues for commitRoot. — [src/render.js#L24-58](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/render.js#L24-L58)
- constructNewChildrenArray drops null/boolean/function children, converts primitives to text VNodes, wraps arrays in Fragments, clones reused VNodes, and assigns parent/depth/matching-index metadata. — [src/diff/children.js#L188-245](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/children.js#L188-L245)
- Child matching compares key and type; actual reorders trigger a longest-increasing-subsequence pass that marks matched nodes outside the subsequence for DOM insertion. — [src/diff/children.js#L298-357](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/children.js#L298-L357) [src/diff/children.js#L460-510](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/children.js#L460-L510)
- diff creates class instances or BaseComponent-backed function instances, resolves context providers, assigns props/state, executes render, and recursively diffs the returned children. — [src/diff/index.js#L121-160](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L121-L160) [src/diff/index.js#L260-307](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L260-L307) [src/diff/index.js#L340-352](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L340-L352)
- setState accumulates partial or functional updates in _nextState and schedules mounted components; the render queue deduplicates dirty components and sorts them by VNode depth. — [src/component.js#L35-59](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/component.js#L35-L59) [src/component.js#L206-215](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/component.js#L206-L215) [src/component.js#L223-253](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/component.js#L223-L253)
- Hook calls index component.__hooks._list; reducer dispatch stores _nextValue after an Object.is comparison, and the before-render callback promotes pending values. — [hooks/src/index.js#L60-85](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L60-L85) [hooks/src/index.js#L170-192](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L170-L192) [hooks/src/index.js#L213-234](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L213-L234)
- DOM output uses ownerDocument for node creation, setProperty for property/attribute/event updates, and insert with moveBefore or insertBefore for placement. — [src/diff/index.js#L569-575](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L569-L575) [src/diff/index.js#L637-651](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L637-L651) [src/diff/props.js#L72-100](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/props.js#L72-L100) [src/diff/children.js#L408-430](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/children.js#L408-L430)
- commitRoot applies refs before options._commit and component callbacks; hooks intercept the commit for layout effects and use after-paint queues for passive effects and deferred unmount cleanup. — [src/diff/index.js#L477-496](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L477-L496) [hooks/src/index.js#L107-123](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L107-L123) [hooks/src/index.js#L448-476](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L448-L476)
- useSyncExternalStore reads a server snapshot during SSR or tracked hydration, caches the latest snapshot/getter, and subscribes to changes that force an update when Object.is detects a different value. — [compat/src/render.js#L39-85](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/render.js#L39-L85) [compat/src/render.js#L324-340](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/render.js#L324-L340)
- People Browser fetches Random User JSON, maps login.username to the model identifier, replaces the users array, and renders observer-driven sorted links and profiles; local deletion mutates the model. — [demo/people/store.ts#L26-65](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/people/store.ts#L26-L65) [demo/people/index.tsx#L9-45](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/people/index.tsx#L9-L45) [demo/people/profile.tsx#L49-58](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/people/profile.tsx#L49-L58)

## Behavior traces

The inspected flows cover initial rendering, a concrete Todo interaction, hydration, promise suspension and retry, portal rendering, and demo network loading. Each trace follows named functions across their input, state owner, and output boundary. Preact has library initialization and mounting rather than a server startup lifecycle. State retention in these flows is primarily the VNode/component graph, while the demo reads localStorage only for diagnostic switches.
- Initial mount: createElement → createVNode → render creates the Fragment root → diff selects component or element handling → diffChildren recursively reconciles output → commitRoot attaches refs and invokes callbacks. — [src/create-element.js#L16-33](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/create-element.js#L16-L33) [src/render.js#L31-58](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/render.js#L31-L58) [src/diff/index.js#L340-352](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L340-L352) [src/diff/index.js#L477-496](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L477-L496)
- Demo startup: index.html loads index.jsx → its URL check conditionally calls initDevTools/initDebug → installLogger reads localStorage/URL switches → render mounts App into document.body. — [demo/index.html#L9-12](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/index.html#L9-L12) [demo/index.jsx#L31-42](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/index.jsx#L31-L42) [demo/index.jsx#L192-197](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/index.jsx#L192-L197)
- Todo typing: setProperty registers the input listener → eventProxy invokes setText → setText calls setState with target.value → enqueueRender schedules process → renderComponent diffs the component and commits the controlled input. — [src/diff/props.js#L72-100](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/props.js#L72-L100) [src/diff/props.js#L152-170](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/props.js#L152-L170) [demo/todo.jsx#L8-9](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/todo.jsx#L8-L9) [src/component.js#L129-156](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/component.js#L129-L156)
- Todo addition/removal: addTodo concatenates a counter-keyed item and clears text, or removeTodo filters by data-id → setState → enqueueRender/process → TodoItems.render maps keyed li nodes → diffChildren matches/inserts or unmounts children. — [demo/todo.jsx#L12-45](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/todo.jsx#L12-L45) [src/component.js#L206-253](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/component.js#L206-L253) [src/diff/children.js#L76-110](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/children.js#L76-L110) [src/diff/children.js#L359-373](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/children.js#L359-L373)
- Hook state update: useState delegates to useReducer → dispatch computes nextValue and compares Object.is → component.setState({}) schedules an update → options._render applies _nextValue → the component renders with its new hook value. — [hooks/src/index.js#L200-229](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L200-L229) [hooks/src/index.js#L60-85](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L60-L85) [src/component.js#L35-59](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/component.js#L35-L59) [src/diff/index.js#L278-286](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L278-L286)
- Hydration: hydrate sets MODE_HYDRATE → render ignores the previous VNode root and supplies existing DOM children → diffElementNodes claims matching nodes or creates missing ones → function-valued props attach handlers → commitRoot completes mounting. — [src/render.js#L21-58](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/render.js#L21-L58) [src/render.js#L69-77](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/render.js#L69-L77) [src/diff/index.js#L549-583](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L549-L583) [src/diff/index.js#L634-652](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L634-L652)
- Lazy loading: Lazy invokes loader once and throws its pending promise → diff catches and calls options._catchError → the Suspense wrapper locates _childDidSuspend → the boundary schedules fallback output → promise settlement restores parent state and force-updates suspenders. — [compat/src/suspense.js#L241-272](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/suspense.js#L241-L272) [src/diff/index.js#L377-443](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L377-L443) [compat/src/suspense.js#L11-30](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/suspense.js#L11-L30) [compat/src/suspense.js#L136-200](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/suspense.js#L136-L200)
- Suspended hydration: diff records suspension flags and a start marker without discarding claimed markup → promise settlement schedules retry → diff rescans nested $s and /$s comment markers → resumed rendering removes only retained DOM it did not claim. — [src/diff/index.js#L78-108](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L78-L108) [src/diff/index.js#L361-366](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L361-L366) [src/diff/index.js#L383-430](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L383-L430) [compat/src/suspense.js#L163-200](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/suspense.js#L163-L200)
- Portal flow: createPortal creates a Portal VNode carrying _parentDom → diff switches container and namespace, resets hydration inputs, and calls diffChildren there → it clears the portal's host _dom and restores the host insertion cursor. — [src/create-portal.js#L9-19](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/create-portal.js#L9-L19) [src/diff/index.js#L309-358](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L309-L358)
- People loading/navigation: App.componentDidMount → store.loadUsers → fetch/json/map → users.replace → observer render generates profile links → Link.handleClick → Router.navigate/history.pushState → nested Route renders Profile. — [demo/people/index.tsx#L9-53](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/people/index.tsx#L9-L53) [demo/people/store.ts#L47-58](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/people/store.ts#L47-L58) [demo/people/router.tsx#L39-96](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/people/router.tsx#L39-L96) [demo/people/router.tsx#L130-150](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/people/router.tsx#L130-L150)
- Unmount: render(null, container) removes unmatched children → unmount invokes options.unmount, ref cleanup, and componentWillUnmount → it recursively removes DOM and clears ownership/listeners; hooks defer passive cleanup through their after-paint queue. — [compat/src/index.js#L104-109](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/index.js#L104-L109) [src/diff/children.js#L359-373](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/children.js#L359-L373) [src/diff/index.js#L755-796](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L755-L796) [hooks/src/index.js#L127-160](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L127-L160)

## Testing signals

Tests and CI were inspected, not executed; no passing result, coverage percentage, or benchmark outcome is asserted. Vitest divides node/shared tests from browser suites driven by headless Chromium through Playwright. The configuration can test source or minified bundles, while separate TypeScript commands check declarations. Inspected tests cover DOM output and operation counts, hydration, component construction, suspense, effects, diagnostics, and serialization guards. The inventory and sampled test reading are bounded rather than exhaustive, and explicit skips identify gaps.
- Vitest uses global test APIs and a node/shared project plus browser/shared suites for core, debug, devtools, hooks, compat, test-utils, and jsx-runtime. — [vitest.config.mjs#L167-170](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/vitest.config.mjs#L167-L170) [vitest.config.mjs#L198-225](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/vitest.config.mjs#L198-L225)
- Browser tests use the Playwright provider with one headless Chromium instance; this configuration does not define Firefox or WebKit instances. — [vitest.config.mjs#L214-221](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/vitest.config.mjs#L214-L221)
- MINIFY=true selects dist aliases and COVERAGE=true enables V8 reports in HTML, LCOV, and text-summary formats; tests transform private property names using mangle.json. — [vitest.config.mjs#L7-8](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/vitest.config.mjs#L7-L8) [vitest.config.mjs#L34-85](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/vitest.config.mjs#L34-L85) [vitest.config.mjs#L89-99](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/vitest.config.mjs#L89-L99) [vitest.config.mjs#L171-196](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/vitest.config.mjs#L171-L196)
- Core render tests assert text/element transitions and template content reuse; Fragment tests record DOM operation sequences, and component tests check class/function invocation arguments. — [test/browser/render.test.jsx#L60-124](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test/browser/render.test.jsx#L60-L124) [test/browser/fragments.test.jsx#L87-111](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test/browser/fragments.test.jsx#L87-L111) [test/browser/components.test.jsx#L53-82](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test/browser/components.test.jsx#L53-L82)
- Hydration tests assert textarea synchronization, existing-DOM reuse, zero mutation logs for matching markup, and event activation after hydration; portal tests assert host-node identity and independent cleanup. — [test/browser/hydrate.test.jsx#L65-114](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test/browser/hydrate.test.jsx#L65-L114) [test/browser/portals.test.jsx#L802-849](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test/browser/portals.test.jsx#L802-L849)
- Suspense tests control promise resolution to assert fallback-to-content behavior; suspense-hydration tests assert unchanged markup and no DOM operations while suspended. — [compat/test/browser/suspense.test.jsx#L81-110](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/test/browser/suspense.test.jsx#L81-L110) [compat/test/browser/suspense-hydration.test.jsx#L81-102](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/test/browser/suspense-hydration.test.jsx#L81-L102)
- useEffect tests assert pending-effect cancellation on unmount and cleanup-before-create ordering; act tests assert options restoration and flushing of effects and event-triggered updates. — [hooks/test/browser/useEffect.test.jsx#L44-83](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/test/browser/useEffect.test.jsx#L44-L83) [test-utils/test/shared/act.test.jsx#L29-68](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test-utils/test/shared/act.test.jsx#L29-L68)
- Debug tests spy on console output, verify devtools attachment, and reject invalid render parents/types; the node suite checks core build exports. — [debug/test/browser/debug.test.jsx#L26-94](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/debug/test/browser/debug.test.jsx#L26-L94) [test/node/index.test.js#L1-14](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test/node/index.test.js#L1-L14)
- Security-relevant regression assertions reject JSON-shaped VNode injection and verify escaping of dynamic JSX attribute/child values; their existence is not evidence that they pass at this commit. — [test/browser/render.test.jsx#L161-179](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test/browser/render.test.jsx#L161-L179) [jsx-runtime/test/browser/jsx-runtime.test.js#L141-151](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/jsx-runtime/test/browser/jsx-runtime.test.js#L141-L151) [jsx-runtime/test/browser/jsx-runtime.test.js#L188-217](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/jsx-runtime/test/browser/jsx-runtime.test.js#L188-L217)
- Core and compat declaration checks use strict TypeScript configurations; DOM declaration fixtures include @ts-expect-error checks for invalid ARIA roles and signal-wrapped attributes. — [package.json#L108-110](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/package.json#L108-L110) [test/ts/tsconfig.json#L2-20](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test/ts/tsconfig.json#L2-L20) [compat/test/ts/tsconfig.json#L2-16](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/test/ts/tsconfig.json#L2-L16) [test/ts/dom-attributes.test-d.tsx#L21-56](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test/ts/dom-attributes.test-d.tsx#L21-L56)
- Explicitly skipped tests concern updating suspended children and successfully unmounting a constantly throwing ref; they cannot supply active regression protection. — [compat/test/browser/suspense.test.jsx#L2054-2057](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/test/browser/suspense.test.jsx#L2054-L2057) [compat/test/browser/suspense.test.jsx#L2120-2123](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/test/browser/suspense.test.jsx#L2120-L2123) [compat/test/browser/suspense.test.jsx#L2187-2190](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/test/browser/suspense.test.jsx#L2187-L2190) [test/browser/lifecycles/componentDidCatch.test.jsx#L915-931](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test/browser/lifecycles/componentDidCatch.test.jsx#L915-L931)
- The inspected build-test workflow specifies frozen installation, build, lint, test:unit with coverage, Coveralls reporting, and packaging without pack lifecycle scripts; none of these steps was executed during this investigation. — [.github/workflows/build-test.yml#L40-63](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/build-test.yml#L40-L63) [package.json#L104-110](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/package.json#L104-L110)
- Benchmark workflows compare local/base tarballs through a benchmark submodule and upload JSON/log artifacts; those workflow definitions provide no measured result in this brief. — [.github/workflows/benchmarks.yml#L13-31](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/benchmarks.yml#L13-L31) [.github/workflows/run-bench.yml#L34-48](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/run-bench.yml#L34-L48) [.github/workflows/run-bench.yml#L73-110](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/run-bench.yml#L73-L110)

## Risk map

The security-sensitive runtime boundaries inspected are raw HTML assignment, DOM attributes/events, VNode identity checks, compiler serialization overrides, and the devtools attachment hook. Renderer correctness also depends on cross-package property mappings and careful coordination of suspension, hydration, portals, refs, and effects. Tests contain explicit skips, and workflow source exposes concrete wiring and artifact-handling concerns. These are source observations or labeled hypotheses, not confirmed vulnerabilities or reproduced runtime failures; no whole-repository absence of auth, IPC, secrets, or other integrations is claimed.
- dangerouslySetInnerHTML assigns newHtml.__html directly to dom.innerHTML without sanitization in this path; applications must control the supplied HTML trust boundary. — [src/diff/index.js#L655-666](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L655-L666) [src/index.d.ts#L66-70](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/index.d.ts#L66-L70)
- setProperty writes DOM properties or attributes directly, including href through the attribute route; no URL-scheme validation appears in this function. — [src/diff/props.js#L101-143](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/props.js#L101-L143)
- The renderer rejects VNodes whose constructor is not undefined, and both factories establish that marker; changing the marker or coercion logic touches the documented JSON-injection defense. — [src/diff/index.js#L74-76](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L74-L76) [src/create-element.js#L86-103](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/create-element.js#L86-L103) [jsx-runtime/src/index.js#L49-62](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/jsx-runtime/src/index.js#L49-L62) [test/browser/render.test.jsx#L161-179](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test/browser/render.test.jsx#L161-L179)
- jsxAttr accepts a string returned by options.attr before default entity encoding, so custom serializers control a raw serialized-HTML output boundary. — [jsx-runtime/src/index.js#L113-148](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/jsx-runtime/src/index.js#L113-L148) [jsx-runtime/test/browser/jsx-runtime.test.js#L154-159](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/jsx-runtime/test/browser/jsx-runtime.test.js#L154-L159)
- Devtools attachment invokes a global __PREACT_DEVTOOLS__.attachPreact with shared options and component primitives; that bridge assumes the installed global hook is trusted. — [devtools/src/devtools.js#L3-20](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/devtools/src/devtools.js#L3-L20)
- Private properties shared by independently built bundles require stable mangle mappings; the build warns about newly mangled properties lacking cache entries, making mapping changes a cross-addon compatibility hotspot. — [mangle.json#L2-4](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/mangle.json#L2-L4) [mangle.json#L27-77](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/mangle.json#L27-L77) [scripts/build.mjs#L108-117](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/scripts/build.mjs#L108-L117) [CONTRIBUTING.md#L77-79](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/CONTRIBUTING.md#L77-L79)
- Hooks, debug, compat normalization, and Suspense all replace callbacks on the same options object while retaining previous callbacks; integration behavior depends on preserving those chains. — [hooks/src/index.js#L32-48](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L32-L48) [debug/src/debug.js#L70-76](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/debug/src/debug.js#L70-L76) [compat/src/render.js#L284-320](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/render.js#L284-L320) [compat/src/suspense.js#L11-41](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/suspense.js#L11-L41)
- Hypothesis: suspension/hydration/portal edits have a broad correctness impact because marker rescanning, DOM cursors, alternate containers, retained hook state, and cleanup routing interact across core and addons. (inferred) — [src/diff/index.js#L78-108](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L78-L108) [src/diff/index.js#L309-358](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L309-L358) [compat/src/suspense.js#L45-82](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/suspense.js#L45-L82) [hooks/src/index.js#L127-160](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L127-L160)
- Three inspected suspended-child update cases and a throwing-ref unmount case are skipped; this is an explicit regression-coverage gap, not proof of current failure. — [compat/test/browser/suspense.test.jsx#L2054-2057](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/test/browser/suspense.test.jsx#L2054-L2057) [compat/test/browser/suspense.test.jsx#L2120-2123](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/test/browser/suspense.test.jsx#L2120-L2123) [compat/test/browser/suspense.test.jsx#L2187-2190](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/test/browser/suspense.test.jsx#L2187-L2190) [test/browser/lifecycles/componentDidCatch.test.jsx#L915-931](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test/browser/lifecycles/componentDidCatch.test.jsx#L915-L931)
- Compat transition APIs do not provide concurrent scheduling: startTransition calls immediately, useDeferredValue returns its input, useTransition reports false, and useInsertionEffect aliases useLayoutEffect. — [compat/src/hooks.js#L3-17](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/hooks.js#L3-L17)
- The manual single-benchmark workflow calls ci.yml as a reusable workflow with ref/artifact_name inputs, but ci.yml declares no workflow_call trigger; it also renames the local tarball before a subsequent rm -rf * step. — [.github/workflows/single-bench.yml#L39-49](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/single-bench.yml#L39-L49) [.github/workflows/single-bench.yml#L59-81](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/single-bench.yml#L59-L81) [.github/workflows/ci.yml#L3-12](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/ci.yml#L3-L12)
- People Browser consumes external Random User JSON without an explicit HTTP status check before parsing/model insertion; malformed responses or model incompatibility can interrupt this demo flow. — [demo/people/store.ts#L36-58](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/people/store.ts#L36-L58) [demo/people/index.tsx#L11-12](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/people/index.tsx#L11-L12)
- The browser configuration selects Chromium only and contains an isolation-leak TODO; browser portability and global addon-state isolation therefore need validation beyond the inspected configuration. (inferred) — [vitest.config.mjs#L214-221](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/vitest.config.mjs#L214-L221) [hooks/src/index.js#L32-37](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L32-L37)
- The PR reporter crosses into authenticated GitHub API/comment operations and downloaded benchmark artifacts; release automation has contents-write and id-token-write jobs, making these CI integration boundaries sensitive to workflow edits. — [.github/workflows/pr-reporter.yml#L32-44](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/pr-reporter.yml#L32-L44) [.github/workflows/pr-reporter.yml#L89-106](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/pr-reporter.yml#L89-L106) [.github/workflows/release.yml#L18-30](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/release.yml#L18-L30) [.github/workflows/release.yml#L52-60](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/release.yml#L52-L60)

## Extension points

Preact's main extension mechanism is the shared options object, rather than an application plugin registry. Renderer callbacks let addons intercept VNode creation, rendering, commits, errors, events, and teardown. Components, context providers, memo comparators, lazy loaders, and external-store subscribers supply narrower behavior contracts. Packaging and demo routes have explicit lists that must be updated when adding a new surface.
- Public Options declares vnode, unmount, diffed, event, requestAnimationFrame, debounceRendering, debug-value, hook-name, and attribute-serialization hooks. — [src/index.d.ts#L305-324](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/index.d.ts#L305-L324) [src/options.js#L3-16](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/options.js#L3-L16)
- Internal Options adds _root, _diff, _commit, _render, _hook, _skipEffects, _catchError, and _hydrationMismatch contracts. — [src/internal.d.ts#L27-51](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/internal.d.ts#L27-L51)
- Hooks demonstrates callback composition by capturing old options handlers and delegating to them; extensions should follow this existing chaining contract. — [hooks/src/index.js#L32-56](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L32-L56) [hooks/src/index.js#L90-123](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L90-L123)
- options.debounceRendering overrides component queue scheduling, and options.requestAnimationFrame overrides passive-effect flushing. — [src/component.js#L206-215](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/component.js#L206-L215) [hooks/src/index.js#L513-517](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L513-L517)
- Component classes extend lifecycle and error-boundary behavior through render, shouldComponentUpdate, getChildContext, getDerivedStateFromError, and componentDidCatch. — [src/diff/index.js#L206-215](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L206-L215) [src/diff/index.js#L267-294](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L267-L294) [src/diff/catch-error.js#L26-48](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/catch-error.js#L26-L48)
- memo accepts a custom equality comparator; lazy accepts an asynchronous loader resolving to a default export or component. — [compat/src/memo.js#L11-25](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/memo.js#L11-L25) [compat/src/suspense.js#L241-272](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/suspense.js#L241-L272)
- useSyncExternalStore integrates external state through subscribe/getSnapshot/getServerSnapshot callbacks and uses the subscription return value as effect cleanup. — [compat/src/render.js#L39-76](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/render.js#L39-L76)
- options.attr can customize compiler-generated attribute serialization, and addHookName forwards labels through options._addHookName. — [jsx-runtime/src/index.js#L113-117](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/jsx-runtime/src/index.js#L113-L117) [devtools/src/index.js#L10-14](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/devtools/src/index.js#L10-L14) [src/index.d.ts#L320-324](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/index.d.ts#L320-L324)
- A new shipped addon must join PACKAGES and supply source/module manifest metadata; the root exports and files lists determine consumer resolution and tarball inclusion. — [scripts/build.mjs#L23-31](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/scripts/build.mjs#L23-L31) [scripts/build.mjs#L204-215](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/scripts/build.mjs#L204-L215) [package.json#L14-87](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/package.json#L14-L87) [package.json#L122-158](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/package.json#L122-L158)
- New demo surfaces plug into imports, navigation links, and Router children in demo/index.jsx; the People router additionally accepts Route component or render callbacks. — [demo/index.jsx#L1-29](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/index.jsx#L1-L29) [demo/index.jsx#L74-88](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/index.jsx#L74-L88) [demo/index.jsx#L149-178](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/index.jsx#L149-L178) [demo/people/router.tsx#L68-96](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/people/router.tsx#L68-L96)
- Type extensions operate through exported VNode/component/attribute interfaces and SignalLike/Signalish contracts; declaration fixtures exercise custom typed DOM attributes. — [src/index.d.ts#L13-21](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/index.d.ts#L13-L21) [src/index.d.ts#L57-70](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/index.d.ts#L57-L70) [src/dom.d.ts#L54-62](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/dom.d.ts#L54-L62) [test/ts/dom-attributes.test-d.tsx#L1-28](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test/ts/dom-attributes.test-d.tsx#L1-L28)

## Contributor handoff

This brief is tied to the pinned commit and should be revalidated against any later checkout. Tests and CI were inspected, not executed; suggested commands below are future validation guidance, not completed checks. The investigation sampled source and tests extensively but does not establish exhaustive coverage or confirmed vulnerabilities. Keep edits localized to the relevant core/addon contract, then consider declarations, VNode factories, property mappings, and bundle behavior where those contracts cross boundaries.
- Read package.json, CONTRIBUTING.md, and workspace configuration first to establish package boundaries, pnpm/Node versions, scripts, and dependency policy; addon manifests are not separate workspace projects. — [package.json#L95-115](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/package.json#L95-L115) [package.json#L215-218](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/package.json#L215-L218) [CONTRIBUTING.md#L14-21](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/CONTRIBUTING.md#L14-L21) [pnpm-workspace.yaml#L1-31](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/pnpm-workspace.yaml#L1-L31)
- For rendering changes, follow src/index.js → render.js → diff/index.js → diff/children.js; the root wrapper, previous tree, recursive diff, and commit queues form the central edit boundary. — [src/index.js#L1-14](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/index.js#L1-L14) [src/render.js#L24-58](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/render.js#L24-L58) [src/diff/index.js#L340-352](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L340-L352) [src/diff/children.js#L76-110](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/children.js#L76-L110)
- Keep the classic and automatic JSX VNode factories synchronized when changing internal shape or ref behavior, including the undefined constructor marker. — [src/create-element.js#L82-103](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/create-element.js#L82-L103) [jsx-runtime/src/index.js#L28-70](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/jsx-runtime/src/index.js#L28-L70) [src/diff/index.js#L74-76](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L74-L76)
- Treat _children, _parent, _depth, _index, _dom, and _flags as coordinated renderer metadata; child matching temporarily uses _index before assigning the final position. — [src/create-element.js#L91-99](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/create-element.js#L91-L99) [src/diff/children.js#L88-96](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/children.js#L88-L96) [src/diff/children.js#L233-245](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/children.js#L233-L245)
- Read component queue scheduling and hooks before changing batching; enqueueRender assumes asynchronous scheduling, and pending hook values are promoted by options._render. — [src/component.js#L186-215](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/component.js#L186-L215) [src/component.js#L226-253](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/component.js#L226-L253) [hooks/src/index.js#L60-85](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L60-L85)
- Read Suspense with core hydration and portal branches before editing async rendering; parked hook state, refs, DOM containers, and stream markers span multiple modules. — [compat/src/suspense.js#L45-82](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/suspense.js#L45-L82) [compat/src/suspense.js#L136-200](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/suspense.js#L136-L200) [src/diff/index.js#L78-108](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L78-L108) [src/diff/index.js#L309-358](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L309-L358)
- Preserve previous options callbacks when extending addon hooks, and restore test scheduler overrides; setupRerender/act/teardown provide the established controls. — [hooks/src/index.js#L32-48](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L32-L48) [test-utils/src/index.js#L7-10](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test-utils/src/index.js#L7-L10) [test-utils/src/index.js#L56-85](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test-utils/src/index.js#L56-L85) [test-utils/src/index.js#L117-129](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test-utils/src/index.js#L117-L129)
- Cross-bundle private-field changes require reviewing mangle.json and build warnings; minified test aliases and property-renaming transforms are intended to exercise that interoperability. — [mangle.json#L27-77](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/mangle.json#L27-L77) [scripts/build.mjs#L108-117](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/scripts/build.mjs#L108-L117) [vitest.config.mjs#L34-85](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/vitest.config.mjs#L34-L85) [vitest.config.mjs#L89-99](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/vitest.config.mjs#L89-L99)
- When execution is authorized, use the relevant file-filtered test:vitest invocation first; declaration changes call for test:ts:core or test:ts:compat, and dist validation requires build before test:vitest:min. These scripts and CI were inspected, not executed here. — [package.json#L97-110](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/package.json#L97-L110) [CONTRIBUTING.md#L111-125](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/CONTRIBUTING.md#L111-L125) [.github/workflows/build-test.yml#L40-47](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/build-test.yml#L40-L47)
- Do not equate the tsc lint command with full runtime-JavaScript checking: jsconfig-lint.json sets checkJs=false and limits its included surfaces; separate strict configs check core and compat declaration fixtures. — [jsconfig-lint.json#L2-18](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/jsconfig-lint.json#L2-L18) [test/ts/tsconfig.json#L2-20](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test/ts/tsconfig.json#L2-L20) [compat/test/ts/tsconfig.json#L2-16](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/test/ts/tsconfig.json#L2-L16)
- Follow formatting conventions of tabs, single quotes, semicolons, no trailing commas, and 80-column print width; use existing oxfmt/oxlint scripts. — [.oxfmtrc.json#L3-15](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.oxfmtrc.json#L3-L15) [package.json#L111-115](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/package.json#L111-L115)
- Read source when contributor prose drifts: CONTRIBUTING describes options.js as an empty object, but the inspected file initializes _catchError; compat/debug peer metadata also still targets preact ^10.0.0 despite the root 11.0.0 version. — [CONTRIBUTING.md#L81-83](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/CONTRIBUTING.md#L81-L83) [src/options.js#L12-14](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/options.js#L12-L14) [compat/package.json#L13-15](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/package.json#L13-L15) [debug/package.json#L13-15](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/debug/package.json#L13-L15)
- Review manual benchmark automation before using it: single-bench.yml calls ci.yml with an unsupported reusable-workflow interface and clears files after producing preact-local.tgz. — [.github/workflows/single-bench.yml#L39-49](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/single-bench.yml#L39-L49) [.github/workflows/single-bench.yml#L59-81](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/single-bench.yml#L59-L81) [.github/workflows/ci.yml#L3-12](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/ci.yml#L3-L12)
- Installation is not a passive read: prepare invokes Husky and Chromium installation, while workspace settings restrict dependency age, provenance trust, engines, and build scripts. — [package.json#L95-103](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/package.json#L95-L103) [pnpm-workspace.yaml#L6-31](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/pnpm-workspace.yaml#L6-L31)

Source repository: https://github.com/preactjs/preact
Full evidence corpus: https://github.com/Codevetter/codevetter/tree/main/benchmarks/repo-unpacks/preact.json

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
