Read the root AGENTS.md first. This file adds jsx-specific rules.
The JSX runtime and React-style hooks: useState, useEffect, useRef, useMemo, useCallback, useReducer, useId, useImperativeHandle, useModal, useSubprocess. Plus the reconciler and the App render loop.
- Most core hooks live in
src/hooks.ts. Standalone hooks live insrc/hooks/<useName>.tswith a sibling<useName>.test.ts. - Export the hook and its types from
src/index.ts. - A hook stores state in a fiber slot. Follow the existing
useStateanduseReducerpatterns exactly:const idx = fiber.hookIndex++thenif (idx >= fiber.hooks.length) fiber.hooks.push(...).
Drive hooks with the fiber test harness, not a real app:
import { createFiber, setCurrentFiber, clearCurrentFiber } from '../hooks.js';
const fiber = createFiber();
setCurrentFiber(fiber);
const result = useMyHook();
clearCurrentFiber();To test a re-render, reset fiber.hookIndex = 0 and call again with the same fiber. See src/hooks/useReducer.test.ts for the full pattern.
- Do not call
Bun.spawnor import frombun. Usenode:child_processso published code runs on Node. - Clean up side effects. A hook that opens something registers cleanup so it closes on unmount.
- The render loop lives in
src/render.ts. Changing it is "ask first" territory.
bun vitest run packages/jsx