How a game works
The split with the client
/src/game.ts decides what is legal and what happens. /src/frontend.tsx decides what the player sees. The client never re-implements a rule: it calls useAvailableActions() and useButtons(), which run your functions on the current state.
If you find yourself writing "is this move allowed" in the client, it belongs here instead.
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 callcreateRoot. - Do not import
reactfor rendering primitives you don't need.reactis available for hooks and types, but React itself is provided by the platform and version-locked : your bundle must not ship its own copy.