# Boardgame.io React Projects – Architecture & Best Practices

## Architectural Patterns & Separation of Concerns  
**Boardgame.io** enforces a clear split between game logic and UI. Game rules are defined in pure **game logic functions** (moves, events, etc.) that operate on the game state (`G`) without side effects ([Concepts - boardgame.io](https://boardgame.io/documentation/#:~:text=They%20must%20not%20depend%20on,is%20handled%20by%20the)). The React front-end (often a component called a *Board*) renders the game state and provides interaction handlers, but does not directly modify game state – it invokes moves instead. This separation is seen across notable projects. For example, Peter Johannessen’s *Yatzy* implementation uses a **game logic module** (defining moves like rolling or scoring dice) and distinct React components for the interface ([Yatzy with boardgame.io | The Lonesome Crowded Web](https://www.lonesomecrowdedweb.com/blog/yatzy-boardgameio/#:~:text=%2A%20Board.tsx%20,of%20dice%20to%20scoring%20categories)). All state mutations (e.g. updating scores or dice) happen in the boardgame.io moves, keeping **game rules out of the UI**. This means a move like `rollDice` is implemented in the game logic, and the UI simply calls `props.moves.rollDice()` when a button is clicked. The boardgame.io docs emphasize that such move functions “must not depend on external state or have any side-effects (except modifying G)” ([Concepts - boardgame.io](https://boardgame.io/documentation/#:~:text=They%20must%20not%20depend%20on,is%20handled%20by%20the)) – in other words, they encapsulate all game rule changes, while the React UI stays stateless with respect to game progress.

**Modular design** is a common theme. Projects often separate the main board rendering from specific UI components. In *Yatzy*, for instance, the UI is divided into `Board.tsx` (the primary container for the game board UI) and a `Scorecard.tsx` component for the score table ([Yatzy with boardgame.io | The Lonesome Crowded Web](https://www.lonesomecrowdedweb.com/blog/yatzy-boardgameio/#:~:text=%2A%20Board.tsx%20,of%20dice%20to%20scoring%20categories)). This keeps the UI organized: the Board component orchestrates overall layout and passes down pieces of state (or callbacks) to child components like dice, cards, or score displays. Other games follow similar patterns – e.g. a chess-like game might have a `Board` component and a `Square` component, a card game might have a `Hand` component, etc. By **modularizing components**, each piece of the UI can focus on presentation and local interaction logic, while the Board component mediates between the game state and these UI parts. 

Notably, *state that is purely visual or interactive (and not part of game rules) is kept out of `G`.* Use React’s own state (`useState`, context, etc.) for things like animations, highlighting, or user interface modes. For example, if a player selects a token, you might store the selected token ID in a React state variable to highlight it – but the actual game state in `G` remains unchanged until a move is made. This approach maintains a **clean separation**: the game state represents the authoritative truth for game rules, and the React state handles transient UI details.

## Handling Drag-and-Drop Interactions  
Implementing drag-and-drop in a boardgame.io React app requires careful coordination between UI libraries and game logic. Many projects leverage libraries like **React DnD** or **react-beautiful-dnd** to handle the low-level drag/drop events and visuals. However, as developers have noted, “implementing simple drag-and-drop functionality for cards [in React] proved to be a nightmare” without the right abstractions ([I've attempted to make the same 2D game prototype in React, Unity, Godot, Construct, Game Maker, and Phaser. Here's what I found. : r/gamedev](https://www.reddit.com/r/gamedev/comments/dcfih6/ive_attempted_to_make_the_same_2d_game_prototype/#:~:text=drop%20functionality%20for%20cards%20proved,to%20be%20a%20nightmare)). The key challenge is ensuring that drag-and-drop interactions obey game rules *without* duplicating those rules in the UI. 

**Best practice:** let the game logic decide what moves are legal, but use the drag/drop library’s APIs to give players feedback on valid moves. For example, with **React DnD** you can define `DropTarget` components with a `canDrop` function. In that function, call a **shared validation function** from your game logic to check if dropping a particular item in a particular spot is a legal move. Boardgame.io’s maintainer suggests factoring out such rule checks into a utility function that both the UI and the game logic can use ([Illegal moves API · Issue #292 · boardgameio/boardgame.io · GitHub](https://github.com/nicolodavis/boardgame.io/issues/292#:~:text=I%20agree%20with%20you%20that,logic%20inside%20your%20view%20layer)). In practice, you might have a function like `canPlacePiece(G, ctx, piece, targetCell)` defined in your game module – the move reducer uses it to validate moves, and the React DnD `canDrop` uses it to highlight allowed drop targets. This ensures you **“DRY” out the game rules**: define them once, use everywhere ([Illegal moves API · Issue #292 · boardgameio/boardgame.io · GitHub](https://github.com/nicolodavis/boardgame.io/issues/292#:~:text=I%20agree%20with%20you%20that,logic%20inside%20your%20view%20layer)). If using **react-beautiful-dnd** (commonly for list/card dragging), you can achieve something similar by enabling/disabling drop zones or by validating in the `onDragEnd` callback. The UI should only call the boardgame.io move (e.g. `moves.playCard(cardId, target)`) if the drop was valid; otherwise it can cancel the action. Many projects opt to pre-filter the drag targets – e.g. highlighting only the slots where a card **could** be dropped – which again is done by querying the game logic. 

By using drag/drop libraries for the heavy lifting, you keep the React components focused on visuals and user input, while the actual state changes still go through boardgame.io moves. A concrete example is a card game: the UI might use react-beautiful-dnd to allow dragging cards from your hand to the play area. All possible drop receptors (play area zones) are rendered as droppable components. When a card is dragged over a zone, the UI could visually indicate “legal” or “illegal” by consulting game logic (e.g. calling a `isMoveLegal(card, zone)` helper). This way, **valid move previewing** happens in the UI with the same logic as the game’s enforcement, so there’s no duplication. If a player somehow tries an invalid drop, the game logic’s move function will simply reject or ignore it. In fact, it’s wise to **double-guard** moves: disable or prevent invalid actions in the UI *and* also check validity in the move implementation. The Yatzy project follows this rule – it disables the “roll” button in the UI after 3 rolls, but still defensively checks in the move handler that no more than 3 rolls occur ([Yatzy with boardgame.io | The Lonesome Crowded Web](https://www.lonesomecrowdedweb.com/blog/yatzy-boardgameio/#:~:text=match%20at%20L266%20In%20addition,3%20rolls%20have%20taken%20place)). This layered validation keeps the game robust.

In summary, **drag-and-drop integration** looks like: use a library to handle drag events, use game logic helpers to filter/validate drops, and on drop, call a boardgame.io move to update state. This keeps game rules centralized while providing a smooth user experience.

## Synchronizing UI with Game Moves  
One of the strengths of boardgame.io is that it automatically syncs the game state to all clients after each move. In a React context, your Board component re-renders with the latest `G` and `ctx` after every move. To keep UI in sync, design your components to derive everything from `G` (or from props passed down). For instance, if a move updates a piece’s coordinates in `G`, the Board and piece components should simply read those coordinates and reflect the new position. Avoid any UI state that tries to track the “source of truth” for game info – let `G` drive it. This approach naturally prevents game rules from leaking into the UI; the UI doesn’t *decide* outcomes, it just renders the outcome from state. 

However, there are cases where the UI needs to show *intermediate* feedback or handle animations that don’t instantly reflect the final state. Two strategies emerge in community projects:

- **Optimistic UI & interim state:** For fast-paced feedback, some UIs use React state to mirror an intended move *briefly* until the state updates. For example, when a piece is dropped onto a board, you might immediately hide it from its old location for a smooth UX, then call the move and let boardgame.io update the real state. This is acceptable as long as the source of truth remains the game state. The interim UI state should get cleared or reconciled once the move is processed. Often, though, boardgame.io is quick enough that such tricks aren’t needed except for animations.

- **Sequencing animations with state updates:** Complex games with animations use patterns to queue state changes and animate in between. One pattern (discussed on HN) is to apply all game logic instantly but **queue animations** so that the UI “catches up” to the game state in a controlled way ([Boardgame.io: an engine for creating turn-based games using JavaScript | Hacker News](https://news.ycombinator.com/item?id=42449497#:~:text=I%20toyed%20with%20an%20approach,animations%20from%20game%20state%20updates)). For example, a card game might play an animation of a card moving and only then reveal the updated state of the card in the new zone. Libraries like **bgio-effects** (a boardgame.io plugin) can help by emitting explicit effect events from moves that the UI listens for ([Tutorial •  bgio-effects](https://delucis.github.io/bgio-effects/tutorial/#:~:text=This%20guide%20shows%20how%20adding,their%20docs%20to%20learn%20more)) ([Tutorial •  bgio-effects](https://delucis.github.io/bgio-effects/tutorial/#:~:text=client.subscribe%28%28%7B%20G%20%7D%29%20%3D,roll)). This decouples visual effects from state logic: the game logic triggers an event “cardPlayed” as part of the move (without breaking purity), and the React side picks that up to run an animation, sound, etc. While not every project uses such plugins, the principle is to **keep game state updates atomic** and handle any staggered presentation purely on the client side.

Another important practice is to **disable UI input during opponent’s turn or during animations** to avoid confusing state changes. Boardgame.io’s `ctx.currentPlayer` and phase mechanisms help here – e.g., your UI can check `props.ctx.currentPlayer` and render other players’ controls as inactive. Some larger projects incorporate a **game context** or provider that supplies global UI state (whose turn it is, what the last move was, etc.) to deep components so they can easily conditionally render. Whether via React Context or simply passing props, ensure that components know what they need from `G`/`ctx` to reflect the correct availability of actions. For instance, a “End Turn” button component might read `ctx.gameover` (to hide itself if the game ended) and use `moves.endTurn` when clicked – it doesn’t need to know any rule beyond whether the game is over or whose turn it is, which are all in the state.

**No game logic in UI:** As a rule, never calculate critical game outcomes in the React layer. The UI can certainly derive things for display (like counting the score from `G` data to show in a scoreboard), but anything that affects the game flow must come from `G` or be enacted via a move. This prevents divergence between what the UI shows and the actual game state. If you find yourself writing a lot of *“if this then that”* in the UI concerning game rules, consider moving that into a move or into a computed property of `G`. A good example is **valid move highlighting** – rather than having the UI independently implement the game’s movement rules to highlight moves, compute those highlights using shared logic or precomputed state. Some games even store **hint information** in `G` (for example, a list of legal moves for the current player) which the UI can then simply map over to highlight options. This is acceptable as long as it’s part of game state and updated by moves. The more common approach, as noted, is to call a shared function to get legal moves on the fly in the UI. Either way, the UI isn’t inventing rules; it’s using rules defined in the game logic.

Finally, robust projects include **redundant checks** to keep UI and logic in sync. The UI should ideally prevent illegal actions (e.g. don’t render a “Draw card” button if the player has no draws left), but the game logic will *also* enforce those rules (e.g. the `drawCard` move would refuse to draw if the player has zero draws remaining). This dual approach, as seen in the Yatzy project, ensures that even if the UI has a bug or a player somehow triggers an out-of-turn action, the game’s integrity stands ([Yatzy with boardgame.io | The Lonesome Crowded Web](https://www.lonesomecrowdedweb.com/blog/yatzy-boardgameio/#:~:text=match%20at%20L266%20In%20addition,3%20rolls%20have%20taken%20place)). It’s a clean separation: the **UI’s responsibility** is to guide and restrict the user for better experience, and the **game logic’s responsibility** is to ultimately validate every move.

## Code Organization & Use of Hooks/Context  
Most boardgame.io React projects are structured to keep things readable and scalable. A common project layout might be: 

- A `game/` folder containing the **game definition** (the object with setup, moves, phases, etc.) and any game-specific logic/helpers (e.g. card data, move validation functions). This is pure logic and can be unit-tested independently.
- A `components/` folder for React components (Board, and subcomponents like Piece, Card, Scoreboard, etc.). Each component has a single responsibility for rendering part of the UI. For instance, the *Elevation of Privilege* project separates components for the card grid and the player hand, with the Board component composing them – making it easier to modify one part without affecting others.
- Optionally, a `client/` or `App.js` where the **boardgame.io Client** is instantiated. Boardgame.io’s React bindings allow you to create a wrapped app by doing `const App = Client({ game: GameLogic, board: BoardComponent, multiplayer: {...} })`. In simpler projects (e.g. the official Tic-Tac-Toe demo), this wrapped component is rendered directly. In more complex apps, you might manually manage the client to integrate with custom routing or state – but even then, the pattern is to keep the boardgame.io client logic in one place (for example, in a custom hook or context provider) and provide the game state and moves to your components.

**React Hooks** are extremely useful in these projects. Hooks like `useEffect` can watch for changes in props (say, a new `G` value) to trigger animations or sounds. Custom hooks can abstract common UI logic – for example, a `useCurrentPlayer()` hook could return whether the current user is the active player, to be used inside many components instead of repeating that check. Some projects also use React Context to avoid prop-drilling the `moves` and `playerID` into every component; a context provider at the top can inject these values so any component can call a move without threading the prop through many layers. This keeps the component interfaces cleaner. For instance, a context might provide `{ G, ctx, moves, playerID, matchID }` globally. This isn’t strictly necessary (boardgame.io’s `<Client>` will pass props to the Board and you can distribute from there), but in larger apps (like those with a separate lobby, or multiple screens), context can help coordinate things like connecting/disconnecting from the game server or showing global UI elements (chat, timers, etc.) that need access to game state.

Another tip from community projects is to leverage TypeScript for better modularity. Many have defined TypeScript types for `G` (game state) and each move’s arguments, which helps keep the game logic and UI contracts clear. For example, the Yatzy project is written in TypeScript and defines a `GameState` type and move function signatures. This means when the UI calls `moves.toggleDie(index)`, TypeScript ensures `index` is the right type and the move exists – catching integration mistakes early.

## Libraries & Tools for Complex Interactions  
Beyond drag-and-drop libraries, developers use various tools to streamline their boardgame.io projects:

- **Immer (immutable state helper)**: Boardgame.io internally uses Immer to handle state changes, so you get immutability for free in moves. But you can also use Immer or Immutable.js in your own logic helpers if needed (some projects do for convenience when calculating next states outside of moves).

- **UI Component Libraries**: To speed up UI development, projects often use libraries like **React Bootstrap** or Material-UI for layout and controls, so they can focus on game-specific UI. The Yatzy project, for example, used Bootstrap 4 for a cleaner layout quickly ([Yatzy with boardgame.io | The Lonesome Crowded Web](https://www.lonesomecrowdedweb.com/blog/yatzy-boardgameio/#:~:text=,Use%20TypeScript)). These libraries don’t affect the game logic separation – they just provide ready-made React components.

- **Canvas/WebGL for boards**: A few games with custom boards (hex grids, 3D boards like *Santorini*) integrate rendering libraries. They keep the same separation principles, but the Board component might draw using HTML5 Canvas or Three.js. For instance, the *Santorini* online project uses Three.js to render a 3D board, but still takes the game state from boardgame.io and converts it to Three.js scene updates. The logic of building or moving is still in boardgame.io moves, while Three.js is purely for visuals. If using such libraries, ensure the rendering layer reads from state and doesn’t maintain its own game state.

- **bgio-effects** and **bgio-client-ai**: `bgio-effects` (by @delucis) helps manage side-effectful events as mentioned, and `bgio-client-ai` can help integrate AI players. These are plugins that keep concerns separate – e.g. AI logic runs in a separate module rather than cluttering your React code.

- **Testing tools**: Some boilerplates set up Jest tests for moves to ensure the game logic works independently. While not a library in the app, this is a best practice to mention: since the game logic is just pure functions, you can write unit tests for each move (e.g., “playing a card should reduce the player’s hand size by 1”) without involving the UI at all. Ensuring game logic correctness makes UI development easier.

- **Starter templates**: To kickstart development, the boardgame.io community has created templates. The official docs site links to a CodeSandbox with many example projects and templates you can fork ([boardgame.io examples - CodeSandbox](https://codesandbox.io/examples/package/boardgame.io#:~:text=Use%20this%20online%20boardgame,below%20to%20run%20it%20instantly)) (from simple tic-tac-toe to more advanced setups). There’s also a community-provided Repl.it template for a basic React + boardgame.io game. These templates illustrate a file/folder structure that you can adopt and then fill in your game specifics. For instance, the CodeSandbox tic-tac-toe example has a `Game.js` for logic and `Board.js` for UI – a minimal, clean starting point.

## Starter Kits & Boilerplates  
If you’re prototyping a new boardgame.io + React project, leveraging existing boilerplates can save time and enforce good structure from the start. A few resources to consider:

- **Boardgame.io Official Examples** – The boardgame.io repo and documentation list a number of notable projects and have links to live demos and source code for many ([boardgame.io/docs/documentation/notable_projects.md at main · boardgameio/boardgame.io · GitHub](https://github.com/boardgameio/boardgame.io/blob/main/docs/documentation/notable_projects.md#:~:text=,code)) ([boardgame.io/docs/documentation/notable_projects.md at main · boardgameio/boardgame.io · GitHub](https://github.com/boardgameio/boardgame.io/blob/main/docs/documentation/notable_projects.md#:~:text=,code)). Exploring these can inspire how to solve common problems. For example, the *Camelot* project shows how to handle a grid-based board, and *Unstable Unicorns Online* (geniegeist’s project) demonstrates managing a complex card game UI with React. While not all are turnkey templates, seeing their code can guide your architecture decisions.

- **CodeSandbox Templates** – As mentioned, the official CodeSandbox playground ([boardgame.io examples - CodeSandbox](https://codesandbox.io/examples/package/boardgame.io#:~:text=Use%20this%20online%20boardgame,below%20to%20run%20it%20instantly)) includes ready-to-run setups. You can fork a basic game and then modify the moves and UI. This is great for small experiments or learning. One of the templates includes a simple lobby and multiple games selection (in case you want a multi-game site).

- **Create React App + boardgame.io** – You can always start with CRA or Vite and add `boardgame.io` as a dependency. The structure would be similar to what the Yatzy tutorial did: quickly scaffold a React app, install boardgame.io, and then create your Game definition and Board component. The Yatzy guide itself outlines this process step by step ([Yatzy with boardgame.io | The Lonesome Crowded Web](https://www.lonesomecrowdedweb.com/blog/yatzy-boardgameio/#:~:text=Getting%20started)) ([Yatzy with boardgame.io | The Lonesome Crowded Web](https://www.lonesomecrowdedweb.com/blog/yatzy-boardgameio/#:~:text=npm%20install%20,components%20%20%23%20Install%20TypeScript)). Following such a guide is akin to using a template, and you’ll end up with a clean, functional base.

- **Community Boilerplates** – Check the boardgame.io GitHub discussions or the subreddit. For example, a user on r/boardgameio shared a Repl.it template that sets up a skeleton game with a basic lobby and room creation. Another community project, **Pong420’s Boardgame** (on GitHub), is an “online board game playground” that includes a lobby, chat, and a structure to plug in new games. Using it as a foundation could jumpstart a multi-game project, though it may be overkill for small prototypes.

When using a boilerplate, ensure it aligns with the best practices above: game logic isolated, UI modular, and so on. Most templates do follow these since they’re built by experienced devs. Still, it’s good to understand the architecture (as this report has detailed) so you can extend the template without breaking the separation of concerns.

## Conclusion & Key Takeaways  
Building React board games with boardgame.io is much easier when you follow the proven patterns from existing projects. In summary:

- **Keep game rules in the game logic layer**, and keep React components focused on rendering and basic input handling. Moves are the only way to change state ([Concepts - boardgame.io](https://boardgame.io/documentation/#:~:text=They%20must%20not%20depend%20on,is%20handled%20by%20the)), and the UI should reflect state, not invent it.  
- **Modularize your components**: a central Board component plus focused subcomponents (hand, piece, board cell, etc.) leads to cleaner code and easier maintenance ([Yatzy with boardgame.io | The Lonesome Crowded Web](https://www.lonesomecrowdedweb.com/blog/yatzy-boardgameio/#:~:text=%2A%20Board.tsx%20,of%20dice%20to%20scoring%20categories)). Use React state or context only for UI-centric info (current selection, animations in progress), not for duplicating game state.  
- **Handle drag-and-drop with specialized tools**, and connect them to your game logic. Use libraries to manage the drag events, but use your game’s move validation to guide what’s allowed (e.g. highlight valid targets by checking with shared rule functions) ([Illegal moves API · Issue #292 · boardgameio/boardgame.io · GitHub](https://github.com/nicolodavis/boardgame.io/issues/292#:~:text=I%20agree%20with%20you%20that,logic%20inside%20your%20view%20layer)). This avoids duplicating logic and ensures the UI always respects actual game rules.  
- **Sync UI and state changes carefully**: Trigger boardgame.io moves for every action and let the state flow down. Where needed, use effects or temporary state for animations, but ultimately let the official state catch up and don’t trust any UI-only state for longer than a single transition. Double-check rules both in UI and logic to be safe ([Yatzy with boardgame.io | The Lonesome Crowded Web](https://www.lonesomecrowdedweb.com/blog/yatzy-boardgameio/#:~:text=match%20at%20L266%20In%20addition,3%20rolls%20have%20taken%20place)).  
- **Use hooks and context to your advantage**: Simplify repetitive logic (like turn checks) with custom hooks, and provide global access to game data if many components need it.  
- **Leverage community resources**: Don’t reinvent the wheel. Examine notable projects (the boardgame.io docs list dozens with links to code) to see how they solved similar challenges – from drag-and-drop card games to complex strategy games with phase logic. Also, start your project with a template or example as a foundation ([boardgame.io examples - CodeSandbox](https://codesandbox.io/examples/package/boardgame.io#:~:text=Use%20this%20online%20boardgame,below%20to%20run%20it%20instantly)); it will already embody many best practices so you can focus on your unique game features.

By following these guidelines and learning from existing projects, you’ll achieve a clean, scalable architecture for your boardgame.io React project – one where adding new features or tweaking rules remains straightforward, and where the separation of concerns keeps your game logic rock-solid and your UI responsive and maintainable.

**Sources:** Notable boardgame.io projects and documentation – e.g. *Yatzy* tutorial (Peter Johannessen) ([Yatzy with boardgame.io | The Lonesome Crowded Web](https://www.lonesomecrowdedweb.com/blog/yatzy-boardgameio/#:~:text=match%20at%20L266%20In%20addition,3%20rolls%20have%20taken%20place)) ([Yatzy with boardgame.io | The Lonesome Crowded Web](https://www.lonesomecrowdedweb.com/blog/yatzy-boardgameio/#:~:text=%2A%20Board.tsx%20,of%20dice%20to%20scoring%20categories)), boardgame.io official docs and issue discussions ([Illegal moves API · Issue #292 · boardgameio/boardgame.io · GitHub](https://github.com/nicolodavis/boardgame.io/issues/292#:~:text=I%20agree%20with%20you%20that,logic%20inside%20your%20view%20layer)) ([Concepts - boardgame.io](https://boardgame.io/documentation/#:~:text=They%20must%20not%20depend%20on,is%20handled%20by%20the)), community insights on React drag-and-drop ([I've attempted to make the same 2D game prototype in React, Unity, Godot, Construct, Game Maker, and Phaser. Here's what I found. : r/gamedev](https://www.reddit.com/r/gamedev/comments/dcfih6/ive_attempted_to_make_the_same_2d_game_prototype/#:~:text=drop%20functionality%20for%20cards%20proved,to%20be%20a%20nightmare)), and the boardgame.io examples/templates repository ([boardgame.io examples - CodeSandbox](https://codesandbox.io/examples/package/boardgame.io#:~:text=Use%20this%20online%20boardgame,below%20to%20run%20it%20instantly)). Each has informed the best practices outlined above.