Skip to content

React UI roots

UI is an explicit React adapter root. Add it to the one project manifest:

A declared React UI root is automatically wrapped in GameProvider and mounted in the shared host stack
The host owns the React root; project code owns the component and JSX document.
vgai.game.json (excerpt)
{
"manifestVersion": 2,
"roots": [
{
"id": "ui",
"adapter": "react",
"entry": "src/ui/game.tsx",
"zOrder": 100
}
]
}

The host owns createRoot, wraps the entry in the engine’s GameProvider, and disposes the React tree with the rest of the game. Game code never creates a second React root.

React subscribes through the engine package:

import { useGameState } from '@engine/react/game-state';
import { Health } from '../scripts/components/health';
export default function GameUI() {
const hp = useGameState((game) => game.queryByComponent(Health)[0]?.hp ?? 0);
return <div data-oid="hud:health">HP {hp}</div>;
}

useGameState re-renders only when the selected value changes; do not poll. The provider is automatic for a declared React root, so project code does not define a provider shim.

React root layers are click-through by default so empty DOM space does not block a canvas below it. Opt interactive elements in explicitly:

<button style={{ pointerEvents: 'auto' }} onClick={restart}>Restart</button>

Elements with stable data-oid values appear beneath the React root in the editor hierarchy. Selecting one exposes React layout and style properties; edits write back to JSX source.