Have more questions? Join our

React frontends

A game ships two source files:

Path Holds
/src/game.ts Required. gameConfig plus the game-logic hooks. The server runs these authoritatively.
/src/frontend.tsx Required. The React client. Its single default export is a React component that renders the whole board.

/src/game.ts holds the rules — pieces, spaces, applyActions, getAvailableActions, scoring, turn passing. /src/frontend.tsx holds everything the player sees. Both files exist from the moment the game is created; the starting template includes them.

The split is strict, and it is what keeps a game honest: the server is the sole authority over shared state. Your frontend never re-implements rules. It reads state and sends clicks; the server decides what those clicks mean, by running the same /src/game.ts you wrote.

The component contract

import { useMatch, useGameState, useAvailableActions } from "boardweaver/react";

export default function App() {
  const match = useMatch();
  const game = useGameState();
  const available = useAvailableActions();

  return (
    <div>
      {game.spaces("plot").map((space) => (
        <button
          key={space.spaceId}
          onClick={() => match.click({ type: "Click", spaceId: space.spaceId })}
        >
          {space.spaceId}
        </button>
      ))}
    </div>
  );
}

Hard requirements:

  • A default export. No default export is a build error, caught at commit time.
  • No props. The platform mounts your component; nothing passes it anything.
  • Never mount anything yourself. The runtime owns the React root. Do not import react-dom, do not call createRoot.
  • Do not import react for rendering primitives you don't need. react is available for hooks and types, but React itself is provided by the platform and version-locked — your bundle must not ship its own copy.

What is deliberately unavailable

Your frontend runs in a sandboxed frame with no network access, on an opaque origin, under a strict Content Security Policy. These are compile errors, and they would fail at runtime even if they weren't:

  • fetch, XMLHttpRequest, WebSocket, EventSource — the frame cannot talk to any server. All data arrives through the hooks.
  • localStorage, sessionStorage, indexedDB, document.cookie — an opaque origin has no persistent storage.
  • window, document, direct DOM access — use React. For frame size use useViewport(), which is authoritative; there is no window worth measuring.

console and timers (setTimeout, setInterval, requestAnimationFrame) work normally.

This is not a restriction to work around — it is the containment boundary that lets untrusted game code run safely in a player's browser. If you find yourself needing one of these, the answer is almost always that the data belongs in game state, where the server owns it.

Assets and presentation

  • useImage(GameImage.Something) resolves a game image to a URL for <img src>. Image keys come from the same per-game GameImage enum exported by "boardweaver" that /src/game.ts uses.
  • useGameTheme() returns the game's validated /theme.json document; pair it with useColorMode() to pick the light or dark side of each color pair.
  • useViewport() gives the frame's current size in CSS pixels and re-renders on change.

Available modules

Only these imports resolve. Bare imports of anything else fail the build.

Module What it is
boardweaver The same runtime the backend uses: GameImage, GameFont, defs, types.
boardweaver/react The match API — every hook in this doc. See react-match-api.
boardweaver/immer Version-locked Immer (produce, produceWithPatches, applyPatches, Patch) for client state. See react-client-state.
boardweaver/dnd Drag and drop primitives.
react, react/jsx-runtime Hooks and types. Provided by the platform.
/src/** Your own tree, by relative or absolute path.

Next

  • react-match-api — every hook, the click lifecycle, and what re-renders when.
  • react-client-state — selection, tentative moves, and undo.
  • build-react-game-instructions — the end-to-end build workflow.
  • v2-game-source — what /src/game.ts must export, and the config object.
  • v2-hooks — every hook's contract, and the action union.
  • v2-entitiesPieceDef / SpaceDef and the three state buckets.
  • v2-game-state — the GameState read/write API, shared with useGameState().