|
| 1 | +# JSX runtime API reference |
| 2 | + |
| 3 | +The functions the in-VM JSX runtime exposes to plugin code (`jsx: true`). Where |
| 4 | +they land in plugin scope — `zushi.useState`, bare, custom — is up to the host; |
| 5 | +see [placement](./placement.md). All entries below are also importable from |
| 6 | +`@reearth/zushi/jsx` for bundled plugins. |
| 7 | + |
| 8 | +Behavior is React-*like* but VM-local: reconciliation and hooks run inside the |
| 9 | +VM, and only a serialized tree crosses to the host (see |
| 10 | +[overview](./overview.md#how-it-works-and-why-its-safe)). |
| 11 | + |
| 12 | +## Elements |
| 13 | + |
| 14 | +### `createElement(type, props?, ...children)` · `h` |
| 15 | + |
| 16 | +Create a virtual node. `type` is an intrinsic tag name (string), a component |
| 17 | +function, or `Fragment`. `h` is an alias. Returns a `VNode`. |
| 18 | + |
| 19 | +```ts |
| 20 | +h("div", { className: "row" }, h("span", null, "hi")) |
| 21 | +``` |
| 22 | + |
| 23 | +### `Fragment` |
| 24 | + |
| 25 | +Groups children without a wrapper element. It's the literal string |
| 26 | +`"__zushi.Fragment"` (so it survives marshaling/bundling), used as a `type`. |
| 27 | + |
| 28 | +### `render(element, options?)` |
| 29 | + |
| 30 | +Mount an element tree into a surface and start reconciling it. |
| 31 | + |
| 32 | +```ts |
| 33 | +render(element, { |
| 34 | + surface?: string, // target surface (default "ui" / the only one) |
| 35 | + visible?: boolean, |
| 36 | + width?: number | string, |
| 37 | + height?: number | string |
| 38 | +}) |
| 39 | +``` |
| 40 | + |
| 41 | +## State |
| 42 | + |
| 43 | +### `useState(initial)` |
| 44 | + |
| 45 | +```ts |
| 46 | +const [value, setValue] = useState(initial /* value | () => value */); |
| 47 | +setValue(next); // value | (prev) => next |
| 48 | +``` |
| 49 | +VM-local, per-component, ephemeral. Updates re-render when the value changes |
| 50 | +(compared with `Object.is`). |
| 51 | + |
| 52 | +### `useReducer(reducer, initialArg, init?)` |
| 53 | + |
| 54 | +```ts |
| 55 | +const [state, dispatch] = useReducer((state, action) => next, initialArg, init?); |
| 56 | +``` |
| 57 | +
|
| 58 | +### `useRef(initial)` |
| 59 | +
|
| 60 | +```ts |
| 61 | +const ref = useRef(initial); // { current }, stable across renders, no re-render on change |
| 62 | +``` |
| 63 | +
|
| 64 | +### Synced state — `useSyncedState`, `useSyncedMap` |
| 65 | +
|
| 66 | +State that lives in the host-owned store instead of VM memory (shared across |
| 67 | +surfaces, host-drivable, optionally persisted). Full docs: |
| 68 | +**[Synced state](./synced.md)**. |
| 69 | +
|
| 70 | +```ts |
| 71 | +const [n, setN] = useSyncedState("count", 0); |
| 72 | +const map = useSyncedMap("items"); // get/has/set/delete/keys/values/entries/size |
| 73 | +``` |
| 74 | +
|
| 75 | +## Effects |
| 76 | +
|
| 77 | +### `useEffect(effect, deps?)` |
| 78 | +
|
| 79 | +```ts |
| 80 | +useEffect(() => { |
| 81 | + // run after render (when deps change, or every render if deps omitted) |
| 82 | + return () => { /* cleanup before next run / on unmount */ }; |
| 83 | +}, [a, b]); |
| 84 | +``` |
| 85 | +Deps are compared with `Object.is`. |
| 86 | +
|
| 87 | +### `useLayoutEffect(effect, deps?)` |
| 88 | +
|
| 89 | +An **alias of `useEffect`** — there's no separate layout phase in the VM. |
| 90 | +
|
| 91 | +## Memoization |
| 92 | +
|
| 93 | +### `useMemo(factory, deps?)` · `useCallback(cb, deps?)` |
| 94 | +
|
| 95 | +```ts |
| 96 | +const value = useMemo(() => compute(a), [a]); |
| 97 | +const cb = useCallback(() => doThing(a), [a]); // === useMemo(() => cb, deps) |
| 98 | +``` |
| 99 | +
|
| 100 | +### `memo(Component, areEqual?)` |
| 101 | +
|
| 102 | +Wrap a component so it re-uses its last render when props are shallow-equal (and |
| 103 | +its own state hasn't changed). `areEqual(prev, next)` overrides the comparison. |
| 104 | +
|
| 105 | +## Context |
| 106 | +
|
| 107 | +### `createContext(defaultValue)` · `useContext(context)` |
| 108 | +
|
| 109 | +```ts |
| 110 | +const Ctx = createContext(defaultValue); |
| 111 | +render(h(Ctx.Provider, { value }, children)); |
| 112 | +const value = useContext(Ctx); // nearest enclosing Provider value, else default |
| 113 | +``` |
| 114 | +
|
| 115 | +## Misc |
| 116 | +
|
| 117 | +### `useId()` |
| 118 | +
|
| 119 | +Returns a stable, per-component-instance id string (for form/aria wiring). |
| 120 | +
|
| 121 | +## Components |
| 122 | +
|
| 123 | +### `ErrorBoundary({ fallback, onError?, children })` |
| 124 | +
|
| 125 | +Catches errors thrown while rendering its subtree and shows `fallback` (a node |
| 126 | +or `(error) => node`) instead; `onError(error)` is called when it catches. |
| 127 | +
|
| 128 | +### `Suspense({ fallback, children })` |
| 129 | +
|
| 130 | +Shows `fallback` while a child throws a thenable, then re-renders when it |
| 131 | +settles. There is no `lazy()` — the VM has no module loader. |
| 132 | +
|
| 133 | +### `registerComponent(name, fn)` |
| 134 | +
|
| 135 | +Registers a trusted custom component (exposed as a bare global by name). Sealed |
| 136 | +from plugin code by default — only the trusted `setup` slot can call it. See |
| 137 | +[Components & intrinsics](./components.md). |
0 commit comments