BOARDGAME.IO DOCUMENTATION
--------------------------


Concepts

State
boardgame.io captures game state in two objects: G and ctx.

{
  // The game state (managed by you).
  G: {},

  // Read-only metadata (managed by the framework).
  ctx: {
    turn: 0,
    currentPlayer: '0',
    numPlayers: 2,
  }
}
These state objects are passed around everywhere and maintained on both client and server seamlessly. The state in ctx is incrementally adoptable, meaning that you can manage all the state manually in G if you so desire.

ctx contains other fields not shown here that games can take advantage of, including support for game phases and complex turn orders.

Because state can be sent between client and server, G must be a JSON-serializable object; in particular, it must not contain classes or functions.

Moves
These are functions that tell the framework how to change G when a particular game move is made. They must not depend on external state or have any side-effects (except modifying G). See the guide on Immutability for how immutability is handled by the framework.

moves: {
  drawCard: ({ G, ctx }) => {
    const card = G.deck.pop();
    G.hand.push(card);
  },

  // ...
}
On the client, you use a moves object to dispatch your move functions.

Plain JS

React
Using React, moves is provided through your component’s props:

props.moves.drawCard();
Events
These are framework-provided functions that are analogous to moves, except that they work on ctx. These typically advance the game state by doing things like ending the turn, changing the game phase etc. Events are dispatched from the client in a similar way to moves.

Plain JS
React
props.events.endTurn();
For more details, see the guide on Events.

Phase
A phase is a period in the game that overrides the game configuration while it is active. For example, you can use a different set of moves or a different turn order during a phase. The game can transition between different phases, and turns occur inside phases. See the guide on Phases for more details.

Turn
A turn is a period of the game that is associated with an individual player. It typically consists of one or more moves made by that player before it passes on to another player. You can also allow other players to play during your turn, although this is less common. See the guide on Turn Orders for more details.

Stage
A stage is similar to a phase, except that it happens within a turn, and applies to individual players rather than the game as a whole. A turn may be subdivided into many stages, each allowing a different set of moves and overriding other game configuration options while that stage is active. Also, different players can be in different stages during a turn. See the guide on Stages for more details.


Tutorial
This tutorial walks through a simple game of Tic-Tac-Toe.

We’re going to be running commands from a terminal and using Node.js/npm. If you haven’t done that before, you might want to read an introduction to the command line and follow the instructions on how to install Node. You’ll also want a text editor to write code in like VS Code or Atom.

Setup
We’re going to use ES2015 features like module imports and the object spread syntax, so we’ll need to use some kind of build system to compile our code for the browser.

This tutorial shows two different approaches: one using React, the other using basic browser APIs and compiling our app with Parcel. You can follow whichever you feel most comfortable with.

Plain JS










React
We’ll use the create-react-app command line tool to initialize our React app and then add boardgame.io to it.

npx create-react-app bgio-tutorial
cd bgio-tutorial
npm install boardgame.io
While we’re here, let’s also create an empty JavaScript file for our game code:

touch src/Game.js
You can check out the complete code for this tutorial and play around with it on CodeSandbox:

Edit boardgame.io

Defining a Game
We define a game by creating an object whose contents tell boardgame.io how your game works. More or less everything is optional, so we can start simple and gradually add complexity. To start, we’ll add a setup function, which will set the initial value of the game state G, and a moves object containing the moves that make up the game.

A move is a function that updates G to the desired new state. It receives an object containing various fields as its first argument. This object includes the game state G and ctx — an object managed by boardgame.io that contains game metadata. It also includes playerID, which identifies the player making the move. After the object containing G and ctx, moves can receive arbitrary arguments that you pass in when making the move.

In Tic-Tac-Toe, we only have one type of move and we will name it clickCell. It will take the ID of the cell that was clicked and update that cell with the ID of the player who clicked it.

Let’s put this together in our src/Game.js file to start defining our game:

export const TicTacToe = {
  setup: () => ({ cells: Array(9).fill(null) }),

  moves: {
    clickCell: ({ G, playerID }, id) => {
      G.cells[id] = playerID;
    },
  },
};
The setup function also receives an object as its first argument like moves. This is useful if you need to customize the initial state based on some field in ctx — the number of players, for example — but we don’t need that for Tic-Tac-Toe.

Creating a Client
Plain JS





React
Replace the contents of src/App.js with

import { Client } from 'boardgame.io/react';
import { TicTacToe } from './Game';

const App = Client({ game: TicTacToe });

export default App;
You can now serve the app from the command line by running:

npm start
Although we haven’t built any UI yet, boardgame.io renders a Debug Panel. This panel means we can already play our Tic-Tac-Toe game!

You can make a move by clicking on clickCell on the Debug Panel, entering a number between 0 and 8, and pressing Enter. The current player will make a move on the chosen cell. The number you enter is the id passed to the clickCell function as the first argument after G and ctx. Notice how the cells array on the Debug Panel updates as you make moves. You can end the turn by clicking endTurn and pressing Enter. The next call to clickCell will result in a “1” in the chosen cell instead of a “0”.


You can turn off the Debug Panel by passing debug: false in the Client config.

Game Improvements
Validating Moves
So far, if a player calls clickCell for a cell that is already filled, it will be overwritten. Let’s prevent that by updating clickCell to let us know that a move is invalid if the selected cell isn’t null.

Moves can let the framework know they are invalid by returning a special constant which we import into src/Game.js:

import { INVALID_MOVE } from 'boardgame.io/core';
Now we can return INVALID_MOVE from clickCell:

clickCell: ({ G, playerID }, id) => {
  if (G.cells[id] !== null) {
    return INVALID_MOVE;
  }
  G.cells[id] = playerID;
}
Managing Turns
In the Debug Panel we clicked endTurn to pass the turn to the next player after making a move. We could do this from our client code too: make a move, then end the turn. This could be flexible because a player could choose when to end their turn, but in Tic-Tac-Toe we know that the turn should always end when a move is made.

There are several different ways to manage turns in boardgame.io. We’ll use the maxMoves option in our game definition to tell the framework to automatically end a player’s turn after a single move has been made, as well as the minMoves option, so players have to make a move and can’t just endTurn.

export const TicTacToe = {
  setup: () => { /* ... */ },

  turn: {
    minMoves: 1,
    maxMoves: 1,
  },

  moves: { /* ... */ },
}
You can learn more in the Turn Order and Events guides.

Victory Condition
The Tic-Tac-Toe game we have so far doesn’t really ever end. Let’s keep track of a winner in case one player wins the game.

First, let’s declare two helper functions in src/Game.js to test the cells array with:

// Return true if `cells` is in a winning configuration.
function IsVictory(cells) {
  const positions = [
    [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6],
    [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]
  ];

  const isRowComplete = row => {
    const symbols = row.map(i => cells[i]);
    return symbols.every(i => i !== null && i === symbols[0]);
  };

  return positions.map(isRowComplete).some(i => i === true);
}

// Return true if all `cells` are occupied.
function IsDraw(cells) {
  return cells.filter(c => c === null).length === 0;
}
Now, we add an endIf method to our game. This method will be called each time our state updates to check if the game is over.

export const TicTacToe = {
  // setup, moves, etc.

  endIf: ({ G, ctx }) => {
    if (IsVictory(G.cells)) {
      return { winner: ctx.currentPlayer };
    }
    if (IsDraw(G.cells)) {
      return { draw: true };
    }
  },
};
endIf takes a function that determines if the game is over. If it returns anything at all, the game ends and the return value is available at ctx.gameover.

Building a Board
Plain JS







React
React can be a good fit for board games because it provides a declarative API to translate objects to UI elements. To create a board we need to translate the game state G into actual cells that are clickable.

Let’s create a new file at src/Board.js:

import React from 'react';

export function TicTacToeBoard({ ctx, G, moves }) {
  const onClick = (id) => moves.clickCell(id);

  let winner = '';
  if (ctx.gameover) {
    winner =
      ctx.gameover.winner !== undefined ? (
        <div id="winner">Winner: {ctx.gameover.winner}</div>
      ) : (
        <div id="winner">Draw!</div>
      );
  }

  const cellStyle = {
    border: '1px solid #555',
    width: '50px',
    height: '50px',
    lineHeight: '50px',
    textAlign: 'center',
  };

  let tbody = [];
  for (let i = 0; i < 3; i++) {
    let cells = [];
    for (let j = 0; j < 3; j++) {
      const id = 3 * i + j;
      cells.push(
        <td key={id}>
          {G.cells[id] ? (
            <div style={cellStyle}>{G.cells[id]}</div>
          ) : (
            <button style={cellStyle} onClick={() => onClick(id)} />
          )}
        </td>
      );
    }
    tbody.push(<tr key={i}>{cells}</tr>);
  }

  return (
    <div>
      <table id="board">
        <tbody>{tbody}</tbody>
      </table>
      {winner}
    </div>
  );
}
The important bit to pay attention to is about how to dispatch moves. We have the following code in our click handler:

moves.clickCell(id);
moves is passed in your component’s props by the framework and contains functions to dispatch your game’s moves. props.moves.clickCell dispatches the clickCell move, and any data passed in is made available in the move handler.
Now, we pass the board component to our Client in src/App.js:

import { TicTacToeBoard } from './Board';

const App = Client({
  game: TicTacToe,
  board: TicTacToeBoard,
});

export default App;
And there you have it. A basic tic-tac-toe game!


You can press 1 (or click on the button next to “reset”) to reset the state of the game and start over.

Bots
In this section we will show you how to add a bot that is capable of playing your game. We need to tell the bot what moves are allowed in the game, and it will find moves that tend to produce winning results.

To do this, add an ai section to the game definition. The enumerate function should return an array of possible moves, so in our case it returns a clickCell move for every empty cell.

export const TicTacToe = {
  // setup, turn, moves, endIf ...

  ai: {
    enumerate: (G, ctx) => {
      let moves = [];
      for (let i = 0; i < 9; i++) {
        if (G.cells[i] === null) {
          moves.push({ move: 'clickCell', args: [i] });
        }
      }
      return moves;
    },
  },
};
That’s it! Now that you can visit the AI section of the Debug Panel:

play causes the bot to calculate and make a single move (shortcut: 2)

simulate causes the bot to play the entire game by itself (shortcut: 3)

play helps you combine moves that you make yourself and bot moves. For example, you can make some manual moves to get two in a row and then verify that the bot makes a block.


The bot uses MCTS under the hood to explore the game tree and find good moves. The default uses 1000 iterations per move. This can be configured to adjust the bot’s playing strength.

The framework will come bundled with a few different bot algorithms, and an advanced version of MCTS that will allow you to specify a set of objectives to optimize for. For example, at any given point in the game you can tell the bot to gather resources in the short term and wage wars in the late stages. You just tell the bot what to do and it will figure out the right combination of moves to make it happen!

Detailed documentation about all this is coming soon. Adding bots to games for actual networked play (as opposed to merely simulating moves) is also in the works.


Multiplayer
In this section, we’ll explain how the framework converts your game logic into a multiplayer implementation without requiring you to write any networking or storage layer code. We will continue working with our Tic-Tac-Toe example from the tutorial.

Clients and Masters
A boardgame.io client is what you create using the Client call. You initialize it with your game object (which contains the moves), so it has all the information that is needed to run the game. This is where the story ends in a single player setup.

In a multiplayer setup, clients no longer act as authoritative stores of the game state. Instead, they delegate the running of the game to a game master. In this mode clients emit moves / events, but the game logic runs on the master, which computes the next game state before broadcasting it to other clients.

However, since clients are aware of the game rules, they also run the game in parallel (this is called an optimistic update and is an optimization that provides a lag-free experience). In case a particular client computes the new game state incorrectly, it is overridden by the master eventually, so the entire setup still has a single source of authority. If a move accesses state that is not accessible to the client (for instance secret state), then optimistic updates may need to be disabled for that move. See the secret state documentation for more details.

Local Master
The game master can run completely on the browser. This is useful to set up pass-and-play multiplayer or for prototyping the multiplayer experience without having to set up a server to test it.

To do this import { Local } from 'boardgame.io/multiplayer', and add multiplayer: Local() to the client options. Now you can instantiate as many of these clients in your app as you like and you will notice that they’re all kept in sync, sharing the same state.

Plain JS




React
// src/App.js

import React from 'react';
import { Client } from 'boardgame.io/react';
import { Local } from 'boardgame.io/multiplayer';
import { TicTacToe } from './Game';
import { TicTacToeBoard } from './Board';

const TicTacToeClient = Client({
  game: TicTacToe,
  board: TicTacToeBoard,
  multiplayer: Local(),
});

const App = () => (
  <div>
    <TicTacToeClient playerID="0" />
    <TicTacToeClient playerID="1" />
  </div>
);

export default App;
Edit boardgame.io

You may be wondering what the playerID parameter is from the example above. Clients needs to be associated with a particular player seat in order to make moves in a multiplayer setup. (If a client doesn’t have a playerID it is a spectator that can see the live game state, but can’t actually make any moves.)


In the example above you can play as Player 0 and Player 1 alternately on the two boards. Clicking on a particular board when it is not that player’s turn has no effect.

Storing state in the browser
If you want game state to be saved in the browser using localStorage, you can pass additional options when creating a local master:

Local({
  // Enable localStorage cache.
  persist: true,

  // Set custom prefix to store data under. Default: 'bgio'.
  storageKey: 'bgio',
});
Remote Master
You can also run the game master on a separate server. Any boardgame.io client can connect to this master (whether it is a browser, an Android app etc.) and it will be kept in sync with other clients in realtime.

In order to connect a client to a remote master, we use the multiplayer option again, but this time we import SocketIO instead of Local, and specify the location of the server.

Plain JS


React
import { SocketIO } from 'boardgame.io/multiplayer'

const TicTacToeClient = Client({
  game: TicTacToe,
  board: TicTacToeBoard,
  multiplayer: SocketIO({ server: 'localhost:8000' }),
});
Behind the scenes, the client now sends updates to the remote master via a WebSocket whenever you make a move. Of course, we now need to run a server at the location specified, which is discussed below.

Setting up the server
We’ll create a new file at src/server.js to write our server code.

boardgame.io provides a server module that simplifies running the game master on a Node server. We import that module and configure it with our TicTacToe game object and a list of URL origins we want to allow to connect to the server. Later you would set origins with your game’s domain name, but for now we’ll import a default value that allows any locally served page to connect.

// src/server.js
const { Server, Origins } = require('boardgame.io/server');
const { TicTacToe } = require('./Game');

const server = Server({
  games: [TicTacToe],
  origins: [Origins.LOCALHOST],
});

server.run(8000);
See the Server reference page for more detail on the various configuration options.

Because Game.js is an ES module, we will use esm which enables us to use import statements in a Node environment:

npm install esm
We can then add a new script to our package.json to simplify running the server:

{
  "scripts": {
    "serve": "node -r esm src/server.js"
  }
}
We can now run npm run serve in one terminal to start the server and npm start in another to serve our web app. You can connect multiple clients to the same game by opening your app in several different browser tabs. You will notice that everything is kept in sync as you play (state is not lost even if you refresh the page).

This example still has both players on the same screen. A more natural setup would be to have each client just have a single (but distinct) player.

Plain JS


React
You want one client to render:

<TicTacToeClient playerID="0" />
and another to render:

<TicTacToeClient playerID="1" />
One way to do this is to ask the player which seat they want to take when they open your app and then set the playerID accordingly. You can also use a URL path to determine the player or use a matchmaking lobby.

Complete code from this section is available on CodeSandbox for both React and Plain JS versions. To run the server, you can click File > Export to ZIP to download the project, then run the server and client as described above. Don’t forget to run npm install in the project directory first!

TIP You can also set the playerID to point to any player while prototyping by clicking on the box of that respective player on the debug UI.

Multiple Game Types
You can serve multiple types of games from the same server:

const app = Server({ games: [TicTacToe, Chess] });
For this to work correctly, make sure that each game implementation specifies a name:

const TicTacToe = {
  name: 'tic-tac-toe',
  // ...
};
Game Instances
By default all client instances connect to a game with an ID 'default'. To play a new game instance, you can pass matchID to your client. All clients that use this ID will now see the same game state.

Plain JS


React
<TicTacToeClient matchID="match-id"/>
The matchID, similar to the playerID can again be determined either by a URL path or a lobby implementation.

Storage
The default storage implementation is an in-memory map. If you want something that’s more persistent, you can use one of the available database connectors, or even implement your own.

See the storage docs for more details.



Turn Order
The framework’s default behavior is to pass the turn around in a round-robin fashion. A player makes one or more moves before triggering an endTurn event, which passes the turn to the next player.

Turn order state is maintained in the following fields:

ctx: {
  currentPlayer: '0',
  playOrder: ['0', '1', '2', ...],
  playOrderPos: 0,
}
currentPlayer
This is the owner of the current turn and the only player that can normally make moves during the turn. You may also allow additional players to make moves during the turn using Stages.

playOrder
The default value is ['0', '1', '2', ... ]. You can think of this as the order in which players sit down at the table. A round robin turn order would move currentPlayer through this list in order.

playOrderPos
An index into playOrder. It is the value that is updated by the turn order policy in order to compute currentPlayer. The default behavior is to just increment it in a round-robin fashion. currentPlayer is just playOrder[playOrderPos].

Changing the Turn Order
Changing the game’s turn order is accomplished by using the order option inside the turn section of the game config:

import { TurnOrder } from 'boardgame.io/core';

const game = {
  turn: {
    order: TurnOrder.ONCE,
  },
};
You will typically use one of the presets below. You may also change the turn order at each phase of the game. See the guide on Phases for more details.

Presets
DEFAULT
This is the default round-robin. It is used if you don’t specify any turn order.

RESET
This is similar to DEFAULT, but instead of incrementing the previous position at the beginning of a phase, it will always start from 0.

CONTINUE
This is also similar to DEFAULT, but instead of incrementing the previous position at the beginning of a phase, it will start with the player who ended the previous phase.

ONCE
This is another round-robin, but it goes around only once. After this, the phase ends automatically.

CUSTOM
Round-robin like DEFAULT, but sets playOrder to the provided value.

turn: {
  order: TurnOrder.CUSTOM(['1', '3']),
}
CUSTOM_FROM
Round-robin like DEFAULT, but sets playOrder to the value in a specified field in G.

turn: {
  order: TurnOrder.CUSTOM_FROM('property_in_G'),
}
Ad Hoc
You can also specify the next player during the endTurn event.

endTurn({ next: playerID });
This argument can also be the return value of turn.endIf and works the same way.

Player 3 is made the new player in both examples below:

function Move({ events }) {
  events.endTurn({ next: '3' });
}
const game = {
  turn: {
    endIf: () => ({ next: '3' }),
  },
};
Creating a Custom Turn Order
If the presets above aren’t what you’re looking for, you can create a custom turn order from scratch:

turn: {
  order: {
    // Get the initial value of playOrderPos.
    // This is called at the beginning of the phase.
    first: ({ G, ctx }) => 0,

    // Get the next value of playOrderPos.
    // This is called at the end of each turn.
    // The phase ends if this returns undefined.
    next: ({ G, ctx }) => (ctx.playOrderPos + 1) % ctx.numPlayers,

    // OPTIONAL:
    // Override the initial value of playOrder.
    // This is called at the beginning of the game / phase.
    playOrder: ({ G, ctx }) => [...],
  }
}



Phases
Most games beyond very simple ones tend to have different behaviors at various phases. A game might have a phase at the beginning where players are drafting cards before entering a playing phase, for example.

Each phase in boardgame.io defines a set of game configuration options that are applied for the duration of that phase. This includes the ability to define a different set of moves, use a different turn order etc. Turns happen inside phases.

Card Game
Let us start with a contrived example of a game that has exactly two moves:

draw a card from the deck into your hand.
play a card from your hand onto the deck.
function DrawCard({ G, playerID }) {
  G.deck--;
  G.hand[playerID]++;
}

function PlayCard({ G, playerID }) {
  G.deck++;
  G.hand[playerID]--;
}

const game = {
  setup: ({ ctx }) => ({ deck: 6, hand: Array(ctx.numPlayers).fill(0) }),
  moves: { DrawCard, PlayCard },
  turn: { minMoves: 1, maxMoves: 1 },
};
Notice how we moved the moves out into standalone functions instead of inlining them in the game object.

We’ll ignore the rendering part of this game, but this is how it might look. Note that you can draw or play a card at any time, including taking a card when the deck is empty.


Phases
Now let’s say we want the game to work in two phases:

a first phase where the players only draw cards (until the deck is empty).
a second phase where the players only play cards.
In order to do this, we define two phases. Each phase can specify its own list of moves, which come into effect during that phase:

const game = {
  setup: ({ ctx }) => ({ deck: 6, hand: Array(ctx.numPlayers).fill(0) }),
  turn: { minMoves: 1, maxMoves: 1 },

  phases: {
    draw: {
      moves: { DrawCard },
    },

    play: {
      moves: { PlayCard },
    },
  },
};
A phase that doesn’t specify any moves just uses moves from the main moves section in the game. However, if it does, then the moves section in the phase overrides the global one.

The game doesn’t begin in any of these phases. In order to begin in the “draw” phase, we add a start: true to its config. Only one phase can have start: true.

phases: {
  draw: {
    moves: { DrawCard },
+   start: true,
  },

  play: {
    moves: { PlayCard },
  },
}
Let’s also end the “draw” phase automatically once the deck is empty.

phases: {
  draw: {
    moves: { DrawCard },
+   endIf: ({ G }) => (G.deck <= 0),
+   next: 'play',
    start: true,
  },

  play: {
    moves: { PlayCard },
  },
}
endIf ends the phase that it is defined in when it returns true. The game is returned to a state where no phase is active. However, for this game, we want to move to the “play” phase once the “draw” phase is done. We specify a next option for this, which tells the framework to go to that phase.

Watch our game in action (now with phases). Notice that you can only draw cards in the first phase, and you can only play cards in the second phase.


Setup and Cleanup hooks
You can also run code automatically at the beginning or end of a phase. These are specified just like normal moves in onBegin and onEnd.

phases: {
  phaseA: {
    onBegin: ({ G, ctx }) => { ... },
    onEnd: ({ G, ctx }) => { ... },
  },
};
Hooks like onBegin and onEnd are run only on the server in multiplayer games. Moves, on the other hand, run on both client and server. They are run on the client in order to facilitate a lag-free experience, and are run on the server to calculate the authoritative game state.

Moving between Phases
Using events
The two primary ways of moving between phases are by calling the following events:

endPhase: This ends the current phase and returns the game to a state where no phase is active. If the phase specifies a next option, then the game will move into that phase instead.

setPhase: This ends the current phase and moves the game into the phase specified by the argument.

Using an endIf condition
You can also end a phase by returning a truthy value from its endIf method:

phases: {
  phaseA: {
    next: 'phaseB',
    endIf: ({ G, ctx }) => true,
  },
  phaseB: { ... },
},
Whenever a phase ends, the current player’s turn is first ended automatically.

Setting the next phase dynamically
Instead of setting a phase’s next option with a string, you can provide a function that will return the next phase based on game state at the end of the phase:

phases: {
  phaseA: {
    next: ({ G }) => {
      return G.condition ? 'phaseC' : 'phaseB';
    },
  },
  phaseB: { ... },
  phaseC: { ... },
},
Override Behavior
As observed above, a phase can specify its own moves section which comes into effect when the phase is active. This moves section completely replaces the global moves section for the duration of the phase. The moves may have the same name as their global equivalents, but they are not related to them in any way.

A phase can similarly also override the turn section. You will typically do this if you want to use a different Turn Order during the phase.



Stages
A stage is similar to a phase, except that it happens within a turn. A turn can be subdivided into many stages, each allowing a different set of moves during that stage.

Stages are also useful to allow more than one player to play during a turn. By default, only the currentPlayer is allowed to make moves during a turn. However, some game situations call for moves by other players. For example, the currentPlayer might play a card that requires every other player in the game to discard a card. These discards don’t have to happen in any particular order, and they’re not really separate turns (the currentPlayer can still play other cards before the turn finally ends). Stages are useful in such situations.

Whenever one or more players enters a stage during a turn, then the framework only allows moves from those players (rather than currentPlayer). The players don’t have to all be in the same stage either (each player can be in their own stage). Each player that is in a stage is now considered an “active” player that can make moves as allowed by the stage that they are in.

You can check playerID inside a move to figure out which player made it. This may be necessary in situations where multiple players are active (and could simultaneously make a move).

const move = ({ G, ctx, playerID }) => {
  console.log(`move made by player ${playerID}`);
};
Defining Stages
Stages are defined inside a turn section:

const game = {
  moves: { ... },

  turn: {
    stages: {
      discard: {
        moves: { DiscardCard },
      },
    },
  },
};
The example above defines a single discard stage that players enter when they are required to discard a card. The stage defines its own moves section which specifies what moves a player in that stage can make. This moves section completely overrides the global moves section for players in that stage (players are not allowed to make any moves from the global moves section while they are in that stage). However, if a stage does not contain a moves section, then players can make moves from the global moves.

A move defined in a stage can have the same name as a global move, but it isn’t related to the global equivalent in any way.

Entering Stages
A stage can be entered by calling the setStage event. This takes the player that called the event into the specified stage:

setStage('discard');
Exiting Stages
Exiting a stage is performed by calling the endStage event. This removes the player from the stage that they are currently in and returns them to a state where they aren’t in any stage.

endStage();
It is possible to automatically take a player to another stage when endStage is called. This is done by specifying a next option in the stage config.

stages: {
  A: { next: 'B' },
  B: { next: 'C' },
  C: { next: 'A' },
}
In the example above, endStage will cycle between the three stages.

Advanced
Sometimes you need to move a group of players into a stage (as opposed to just the player that called the event). We use the setActivePlayers event for this:

setActivePlayers({
  // Move the current player to a stage.
  currentPlayer: 'stage-name',

  // Move every other player to a stage.
  others: 'stage-name',

  // Move all players to a stage.
  all: 'stage-name',

  // Enumerate the set of players and the stages that they
  // are in.
  value: {
    '0': 'stage-name',
    '1': 'stage-name',
    ...
  },

  // Prevents manual endStage before the player
  // has made the specified number of moves.
  minMoves: 1,

  // Calls endStage automatically after the player
  // has made the specified number of moves.
  maxMoves: 5,

  // This takes the stage configuration to the
  // value prior to this setActivePlayers call
  // once the set of active players becomes empty
  // (due to players either calling endStage or
  // maxMoves ending the stage for them).
  revert: true,

  // A next option will be used once the set of active players
  // becomes empty (either by using maxMoves or manually removing
  // players).
  // All options available inside setActivePlayers are available
  // inside next.
  next: { ... },
});
Let’s go back to the example we discussed earlier where we require every other player to discard a card when we play one:

function PlayCard({ events }) {
  events.setActivePlayers({ others: 'discard', minMoves: 1, maxMoves: 1 });
}

const game = {
  moves: { PlayCard },
  turn: {
    stages: {
      discard: {
        moves: { Discard },
      },
    },
  },
};

Advanced Move Limits
Passing a minMoves argument to setActivePlayers forces all the active players to make at least that number of moves before being able to end the stage, but sometimes you might want to set different move limits for different players. For cases like this, setStage and setActivePlayers support long-form arguments:

setStage({ stage: 'stage-name', minMoves: 3 });
setActivePlayers({
  currentPlayer: { stage: 'stage-name', minMoves: 2 },
  others: { stage: 'stage-name', minMoves: 1 },
  value: {
    '0': { stage: 'stage-name', minMoves: 4 },
  },
});
Passing a maxMoves argument to setActivePlayers limits all the active players to making that number of moves, but sometimes you might want to set different move limits for different players. For cases like this, setStage and setActivePlayers support long-form arguments:

setStage({ stage: 'stage-name', maxMoves: 3 });
setActivePlayers({
  currentPlayer: { stage: 'stage-name', maxMoves: 2 },
  others: { stage: 'stage-name', maxMoves: 1 },
  value: {
    '0': { stage: 'stage-name', maxMoves: 4 },
  },
});
Stage.NULL
Sometimes you want to add a player to the set of active players but don’t want them to be in a specific stage. You can use Stage.NULL for this:

import { Stage } from 'boardgame.io/core';

// This allows any player to make a move, but doesn't restrict them to
// a particular stage.
setActivePlayers({ all: Stage.NULL });
There is also a convenient syntax to enumerate the players that you want in the set of active players:

// Players 0 and 3 are added to the set of active players,
// and neither is placed in a stage.
setActivePlayers(['0', '3']);
Configuring active players at the beginning of a turn.
You can have setActivePlayers called automatically at the beginning of the turn by adding an activePlayers section to the turn config:

turn: {
  activePlayers: { all: Stage.NULL },
}
Presets
A number of activePlayers configurations are available as presets that you can use directly:

import { ActivePlayers } from 'boardgame.io/core';

turn: {
  activePlayers: ActivePlayers.ALL;
}
ALL
Equivalent to { all: Stage.NULL }. Any player can play, and they aren’t restricted to any particular stage.

ALL_ONCE
Equivalent to { all: Stage.NULL, minMoves: 1, maxMoves: 1 }. Any player can make exactly one move before they are removed from the set of active players.

OTHERS
Similar to ALL, but excludes the current player from the set of active players.

OTHERS_ONCE
Similar to ALL_ONCE, but excludes the current player from the set of active players.




Events
An event is used to advance the game state. It is somewhat analogous to a move, except that while a move changes G, an event changes ctx. Also, events are provided by the framework (as opposed to moves, which are written by you).

Event Types
endStage
This event takes the player that called it out of the stage that they are in. If the definition for the current stage in the game object specifies a next option, then the player is taken to the next stage. If not, the player is returned to a state where they are not in any stage.

endStage();
endTurn
This event ends the turn. The default behavior is to increment ctx.turn by 1 and advance currentPlayer to the next player according to the configured turn order (the default being a round-robin).

This event also accepts an argument, which (if provided) switches the turn to the specified player instead.

endTurn(); // without argument
endTurn({ next: '2' }); // Player 2 is the next player.
endPhase
This event ends the current phase. If the definition for the current phase in the game object specifies a next option, then the game moves to that phase. If not, the game returns to a state where no phase is active.

endPhase();
endGame
This event ends the game. If you pass an argument to it, then that argument is made available in ctx.gameover. After the game is over, further state changes to the game (via a move or event) are not possible.

endGame();
setStage
Takes the player that called the event into the stage specified.

setStage('stage-name');
setPhase
Takes the game into the phase specified. Ends the active phase first.

setPhase('phase-name');
setActivePlayers
Allows adding additional players to the set of “active players”, and also any stages that you want to put them in. See the guide on Stages for more details.

Triggering an event from game logic.
You can trigger events from a move or code inside your game logic (a phase’s onBegin hook, for example). This is done through the events API in the object passed as the first argument to moves:

moves: {
  drawCard: ({ G, ctx, events }) => {
    events.endPhase();
  };
}
Events are queued up and triggered after a move. Any changes you make to G will be applied before events are triggered, even if the event is called first in your move function.

Triggering an event from the client
Plain JS

React
Events are available through props inside the events object. For example:

import React from 'react';

function Board({ events }) {
  const onClick = () => {
    events.endTurn();
  };

  return <button onClick={onClick}>End Turn</button>;
}
Disabling events
Events can be disabled. For example, you might not want a player to be able to end the game directly by simply calling the endGame event.

In order to disable an event, just add eventName: false to the events section in your game config.

const game = {
  events: {
    endGame: false,
    // ...
  },
};
This doesn’t apply to events in moves or hooks, but just the ability to call an event directly from a client.

Calling events from hooks
The events API is available in game hooks like it is inside moves. However, because of how hooks and events interact, certain events cannot be called from certain hooks. The following table at https://boardgame.io/documentation/#/events shows which hooks support which events.



Undo / Redo
boardgame.io comes with built-in support to undo / redo moves in the current turn. This is a common pattern in games that allow a player to make multiple moves per turn, and can be a useful feature to allow the player to experiment with different move combinations (and seeing what they do) before committing to one. You can disable this feature by setting disableUndo to true in the game config.

Usage
You can call the undo and redo functions from the client.

Plain JS

React
The methods are passed in your board component’s props:

props.undo();
props.redo();
Restricting Undoable Moves
In case you just want specific moves to be undoable (to prevent peeking at cards or rerolling of dice, for example), you can use the long-form move syntax, which specifies the move as an object rather than a function. The undoable bit indicates whether the move can be undone:

const game = {
  moves: {
    rollDice: {
      move: ({ G, ctx }) => {},
      undoable: false,
    },

    playCard: ({ G, ctx }) => {},
  },
};
In the example above, playCard will be undoable, but not rollDice.



Randomness
Many games allow moves whose outcome depends on shuffled cards or rolled dice. Take e.g. the game Yahtzee. A player rolls dice, chooses some, rolls another time, chooses some more, and does a final dice roll. Depending on the face-up sides the player now must choose where they will score.

This poses interesting challenges regarding the implementation.

AI. Randomness makes games interesting since you cannot predict the future, but it needs to be controlled in order for allowing games that can be replayed exactly (e.g. for AI purposes).

PRNG State. The game runs on both the server and client. All code and data on the client can be viewed and used to a player’s advantage. If a client could predict the next random numbers that are to be generated, the future flow of a game stops being unpredictable. The library must not allow such a scenario. The RNG and its state must stay on the server.

Pure Functions. The library is built using Redux. This is important for games since each move is a reducer, and thus must be pure. Calling Math.random() and other functions that maintain external state would make the game logic impure and not idempotent.

Using Randomness in Games
The object passed to moves and other game logic contains an object random, which exposes a range of functions for generating randomness.

For example, the random.D6 function is similar to rolling six-sided dice:

{
  moves: {
    rollDie: ({ G, random }) => {
      G.dieRoll = random.D6(); // dieRoll = 1–6
    },

    rollThreeDice: ({ G, random }) => {
      G.diceRoll = random.D6(3); // diceRoll = [1–6, 1–6, 1–6]
    }
  },
}
You can see details for all the available random functions below.

Seed
You can set the initial seed used for the random number generator on your game object:

const game = {
  seed: 42,
  // ...
};
seed can be either a string or a number.

API Reference
1. Die
Arguments
spotvalue (number): The die dimension (default: 6).
diceCount (number): The number of dice to throw.
Returns
The die roll value (or an array of values if diceCount is greater than 1).

Usage
const game = {
  moves: {
    move({ random }) {
      const die = random.Die(6);      // die = 1-6
      const dice = random.Die(6, 3);  // dice = [1-6, 1-6, 1-6]
    },
  }
};
2. Number
Returns a random number between 0 and 1.

Usage
const game = {
  moves: {
    move({ random }) {
      const n = random.Number();
    },
  }
};
3. Shuffle
Arguments
deck (array): An array to shuffle.
Returns
The shuffled array.

Usage
const game = {
  moves: {
    move({ G, random }) {
      G.deck = random.Shuffle(G.deck);
    },
  },
};
4. Wrappers
D4, D6, D8, D10, D12 and D20 are wrappers around Die(n).

Arguments
diceCount (number): The number of dice to throw.
Usage
const game = {
  moves: {
    move({ random }) {
      const die = random.D6();
    },
  }
};




Secret State
In some games you might need to hide information from players or spectators. For example, you might not want to reveal the hands of opponents in card games.

This is easily accomplished at the UI layer (by not rendering secret information), but the framework also provides support for not even sending such data to the client.

In order to do this, use the playerView setting in the game object. It accepts a function that receives an object containing G, ctx, and playerID, and returns a version of G that is stripped of any information that should be hidden from that specific player.

const game = {
  // `playerID` could also be null or undefined for spectators.
  playerView: ({ G, ctx, playerID }) => {
    return StripSecrets(G, playerID);
  },
  // ...
};
Make sure that you associate the game clients with individual players (as discussed in the Multiplayer section).

PlayerView.STRIP_SECRETS
The framework comes bundled with an implementation of playerView that does the following:

It removes a key named secret from G.
If G contains a players object, it removes all keys except for the one that matches playerID.
G: {
  secret: { ... },

  players: {
    '0': { ... },
    '1': { ... },
    '2': { ... },
  }
}
becomes the following for player 1:

G: {
  players: {
    '1': { ... },
  }
}
Usage:

import { PlayerView } from 'boardgame.io/core';

const game = {
  // ...
  playerView: PlayerView.STRIP_SECRETS,
};
Disabling moves that manipulate secret state on the client
Moves that manipulate secret state often cannot run on the client because the client doesn’t have all the necessary data to process such moves. These can be marked as server-only by setting client: false on move:

moves: {
  moveThatUsesSecret: {
    move: ({ G, random }) => {
      G.secret.value = random.Number();
    },

    client: false,
  }
}



Immutability
The principle of immutability as applied to state changing functions like moves in boardgame.io mandates that they be pure functions. What this means is that you cannot depend on any external state, nor can you have any side-effects, i.e. you cannot modify anything that isn’t a local variable (not even the arguments).

The benefits of architecting a system with this principle are that you can ensure repeatability (moves can be replayed over a particular state value multiple times in different places) and you can do cheap comparisons to check if something changed.

A traditional pure function just accepts arguments and then returns the new state. Something like this:

function move({ G }) {
  // Return new value of G without modifying the arguments.
  return { ...G, hand: G.hand + 1 };
}
The example above uses the spread syntax to create a new object.

boardgame.io provides a more convenient syntax by allowing you to mutate G directly while using a library under the hood to convert your move into a pure function that respects the immutability principle. Both styles are supported interchangeably, so use the one that you prefer.

function move({ G }) {
  G.hand++;
}
Note that in this style you do not return the new state. In fact, returning something while also mutating G is considered an error.

You can only modify G. Other values passed to your moves are read-only and should never be modified in either style. Changes to ctx can be made using events.

Invalid moves
In both styles, invalid moves are indicated by returning a special constant. This tells the framework that the current set of arguments passed in is illegal and that the move ought to be discarded. For example, you might do this if the user tries to click on an already filled cell in Tic-Tac-Toe.

import { INVALID_MOVE } from 'boardgame.io/core';

moves: {
  clickCell: function({ G, ctx }, id) {
    // Illegal move: Cell is filled.
    if (G.cells[id] !== null) {
      return INVALID_MOVE;
    }

    // Fill cell with 0 or 1 depending on the current player.
    G.cells[id] = ctx.currentPlayer;
  }
}




Plugins
The Plugin API allows you to create objects that expose custom functionality to boardgame.io. You can create wrappers around moves, add API’s to ctx etc.

Creating a Plugin
A plugin is an object that contains the following fields.

{
  // Required.
  name: 'plugin-name',

  // Initialize the plugin's data.
  // This is stored in a special area of the state object
  // and not exposed to the move functions.
  setup: ({ G, ctx, game }) => data object,

  // Create an object that becomes available in `ctx`
  // under `ctx['plugin-name']`.
  // This is called at the beginning of a move or event.
  // This object will be held in memory until flush (below)
  // is called.
  api: ({ G, ctx, game, data, playerID }) => api object,

  // Return an updated version of data that is persisted
  // in the game's state object.
  flush: ({ G, ctx, game, data, api }) => data object,

  // Function that accepts a move / trigger function
  // and returns another function that wraps it. This
  // wrapper can modify G before passing it down to
  // the wrapped function. It is a good practice to
  // undo the change at the end of the call. 
  // `fnType` gives the type of hook being wrapped
  // and will be one of the `GameMethod` values —
  // import { GameMethod } from 'boardgame.io/core' 
  fnWrap: (fn, fnType) => ({ G, ...rest }, ...args) => {
    G = preprocess(G);
    G = fn({ G, ...rest }, ...args);
    if (fnType === GameMethod.TURN_ON_END) {
      // only run when wrapping a turn’s onEnd function
    }
    G = postprocess(G);
    return G;
  },

  // Function that allows the plugin to indicate that it
  // should not be run on the client. If it returns true,
  // the client will discard the state update and wait
  // for the master instead.
  noClient: ({ G, ctx, game, data, api }) => boolean,

  // Function that allows the plugin to indicate that the
  // current action should be declared invalid and cancelled.
  // If `isInvalid` returns an error message, the whole update
  // will be abandoned and an error returned to the client.
  isInvalid: ({ G, ctx, game, data, api }) => false | string,

  // Function that can filter `data` to hide secret state
  // before sending it to a specific client.
  // `playerID` could also be null or undefined for spectators.
  playerView: ({ G, ctx, game, data, playerID }) => filtered data object,
}
Adding Plugins to Games
The list of plugins is specified in the game spec.

import { PluginA, PluginB } from 'boardgame.io/plugins';

const game = {
  name: 'my-game',

  plugins: [PluginA, PluginB],

  // ...
};
Plugins are applied one after the other in the order that they are specified (from left to right).

Configuring Plugins
Some plugins may need a user to provide some configuration. The recommended way to do that is to design the plugin as a factory function that takes configuration as its arguments and returns a plugin object.

import { ConfigurablePlugin } from './plugins';

const game = {
  name: 'my-game',
  plugins: [
    ConfigurablePlugin(options),
  ],
}
See PluginPlayer below for an example of this in practice.

Available Plugins
PluginPlayer
import { PluginPlayer } from 'boardgame.io/plugins';

// define a function to initialize each player’s state
const playerSetup = (playerID) => ({ ... });

// filter data returned to each client to hide secret state (OPTIONAL)
const playerView = (players, playerID) => ({
  [playerID]: players[playerID],
});

const game = {
  plugins: [
    // pass your function to the player plugin
    PluginPlayer({
      setup: playerSetup,
      playerView: playerView,
    }),
  ],
};
PluginPlayer makes it easy to manage player state. It creates an object players that stores state for individual players. This object is stored in the plugin’s private storage area:

players: {
  '0': { ... },
  '1': { ... },
  '2': { ... },
  ...
}
The initial values of these states are determined by the setup function in its options object, which creates the state for a particular playerID.

The record associated with the current player can be accessed via ctx.player.get(). If this is a 2 player game, then the opponent’s record is available using ctx.player.opponent.get(). These fields can be modified using their corresponding set() versions.

ctx.player.get() // Get the current player's record.
ctx.player.set() // Update the current player's record.
ctx.player.opponent.get() // Get the opponent player's record.
ctx.player.opponent.set() // Update the opponent player's record.



Debugging
Using the Debug Panel in production
boardgame.io comes bundled with a debug panel that lets you interact with your game and game clients. When you build your app for production (i.e. when NODE_ENV === 'production') this is stripped out from the final bundle.

If you want to include the debug panel in a production build you can do so explicitly when creating your client:

import { Debug } from 'boardgame.io/debug';

const client = Client({
  // ...
  debug: { impl: Debug },
});
Debug Panel options
You can use the collapseOnLoad option to hide the panel by default when the client loads. The hideToggleButton option removes the toggle button on the side of the panel which means you can only use the keyboard shortcut to toggle its visibility.

const client = Client({
  // ...
  debug: {
    // ...
    collapseOnLoad: true/false,
    hideToggleButton: true/false
  },
});
Custom metadata in game logs
It can sometimes be helpful to surface some metadata during a move. You can do this by using the log plugin. For example,

const move = ({ log }) => {
  log.setMetadata('metadata for this move');
};
This metadata is stored in the log client property and displayed in the Log section of the debug panel.

Redux
The framework uses Redux under the hood. You may sometimes want to debug this Redux store directly. In order to do so, you can pass along a Redux store enhancer with your client. For example,

import logger from 'redux-logger';
import { applyMiddleware } from 'redux';

Client({
  // ...
  enhancer: applyMiddleware(logger),
});
Doing so will console.log on state changes. This can also hook into the Chrome Redux DevTools browser extension like this:

Client({
  // ...
  enhancer: (
    window.__REDUX_DEVTOOLS_EXTENSION__
    && window.__REDUX_DEVTOOLS_EXTENSION__()
  ),
})
or both

import logger from 'redux-logger';
import { applyMiddleware, compose } from 'redux';

Client({
  // ...
  enhancer: compose(
    applyMiddleware(logger),
    (window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
  ),
})
Server + Sockets
The Koa-server can be debugged by setting the DEBUG environment variable before starting it. This will give you access to logs of incoming requests as well as the socket.io logs. To set the environment variable prepend your npm script to run the server like so:

DEBUG=* node server.js
NOTE: For various debugging scopes have a look at the socket.io-docs



Testing Strategies
Unit Tests
Moves are just functions, so they lend themselves to unit testing. A useful strategy is to implement each move as a standalone function before passing them to the game object:

Game.js

export function clickCell({ G, playerID }, id) {
  G.cells[id] = playerID;
}

export const TicTacToe = {
  moves: { clickCell },
  // ...
}
Game.test.js

import { clickCell } from './Game';

it('should place the correct value in the cell', () => {
  // original state.
  const G = {
    cells: [null, null, null, null, null, null, null, null, null],
  };

  // make move.
  clickCell({ G, playerID: '1' }, 3);

  // verify new state.
  expect(G).toEqual({
    cells: [null, null, null, '1', null, null, null, null, null],
  });
});
Scenario Tests
Test your game logic in specific scenarios.

import { Client } from 'boardgame.io/client';
import { TicTacToe } from './Game';

it('should declare player 1 as the winner', () => {
  // set up a specific board scenario
  const TicTacToeCustomScenario = {
    ...TicTacToe,
    setup: () => ({
      cells: ['0', '0', null, '1', '1', null, null, null, null],
    }),
  };

  // initialize the client with your custom scenario
  const client = Client({
    game: TicTacToeCustomScenario,
  });

  // make some game moves
  client.moves.clickCell(8);
  client.moves.clickCell(5);

  // get the latest game state
  const { G, ctx } = client.getState();

  // the board should look like this now
  expect(G.cells).toEqual(['0', '0', null, '1', '1', '1', null, null, '0']);
  // player '1' should be declared the winner
  expect(ctx.gameover).toEqual({ winner: '1' });
});
Note that we imported the vanilla JavaScript client, not the one from boardgame.io/react.

Testing Randomness
If you are testing a move that uses the Random API, by definition you can’t always expect the same result, making it harder to test. In this case, you can use one of the following strategies.

Fixed PRNG seed
You can set seed in your game object. This will be used to initialise the Random API’s internal state and you’ll see a predictable sequence of results from calls to random API methods:

import { Client } from 'boardgame.io/client';

const Game = {
  moves: {
    rollDice: ({ G, random }) => {
      G.roll = random.D6();
    },
  },
};

it('updates G.roll with a random number', () => {
  const client = Client({
      // Set seed so PRNG always starts in same state
    game: { ...Game, seed: 'fixed-seed' },
  });
  client.moves.rollDice();
  const { G } = client.getState();
  expect(G.roll).toMatchInlineSnapshot(`4`);
});
Override Random API since v0.49.10
If you need to test specific random outcomes, you can override the Random API entirely to allow complete control of the results of API methods.

import { Client } from 'boardgame.io/client';
import { MockRandom } from 'boardgame.io/testing';

// Create a mock of the random plugin, where the D6 method always returns 6.
// Any methods you don’t provide an implementation for will behave as usual.
const randomPlugin = MockRandom({
  D6: () => 6,
});

it ('rolls a six', () => {
  const client = Client({
    game: {
      ...Game,
      // Add the random plugin mock to the game’s plugins.
      plugins: [...(Game.plugins || []), randomPlugin]
    },
  });
  client.moves.rollDice();
  const { G } = client.getState();
  expect(G.roll).toMatchInlineSnapshot(`6`);
});
Multiplayer Tests
Use the local multiplayer mode to simulate multiplayer interactions in unit tests.

it('multiplayer test', () => {
  const spec = {
    game: MyGame,
    multiplayer: Local(),
  };

  const p0 = Client({ ...spec, playerID: '0' });
  const p1 = Client({ ...spec, playerID: '1' });

  p0.start();
  p1.start();

  p0.moves.moveA();
  p0.events.endTurn();

  // Player 1's state reflects the moves made by Player 0.
  expect(p1.getState()).toEqual(...);

  p1.moves.moveA();
  p1.events.endTurn();

  ...
});
Integration Tests
Test the application end-to-end from the UI layer’s point of view.

In this case we use React Testing Library to mount our React component and look for the TicTacToe board inside of it. We then check the board is rendered and responds to user interaction as expected.

import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import App from './app';

describe('Tic-Tac-Toe', () => {
  const { container } = render(<App />);
  const cells = container.querySelectorAll('td');
  
  test('board is empty initially', () => {
    expect(cells).toHaveLength(9);
    for (const cell of cells) {
      expect(cell).toBeEmptyDOMElement();
    }
  });
  
  test('clicking a cell places player 0’s marker', () => {
    fireEvent.click(cells[5]);
    expect(cells[5]).toHaveTextContent('0');
  });
});



Deployment
Serverless Options
For one-player or pass-and-play games, you may not need the boardgame.io game server and prefer to serve an app that runs entirely on the client. If you don’t need multiplayer features, this can be a lot simpler than getting a Node.js server deployed.

There are many services that can help deploy a static app, including some that offer free options like Netlify and Render.

Plain JS




React
Running npm run build in a Create React App project will create an optimised production build in /build, which you can host just about anywhere.

Deployment guides
Netlify: See the guide on how to deploy to Netlify in the Create React App docs.

Render: See “Deploy a Create React App Static Site” in the Render docs.

Heroku
Heroku uses 2 different ways to determine the run command of a node application. It is possible to either:

Add a Procfile to the project root directory with the following line
web: node -r esm server.js

Update the start script in the package.json to
"start": "node -r esm server.js"

On Heroku, a regular heroku/nodejs buildpack is necessary to build your app which is usually selected by default for node applications.

Frontend and Backend
In order to deploy a game to Heroku, the game has to be running on a single port. To do so, the Server has to handle both the API requests and serving the pages.
Below is an example of how to achieve that.

First install these extra dependencies:

npm i koa-static
Then adjust your server.js file like this:

// server.js

import { Server } from 'boardgame.io/server';
import path from 'path';
import serve from 'koa-static';
import { TicTacToe } from './game';

const server = Server({ games: [TicTacToe] });
const PORT = process.env.PORT || 8000;

// Build path relative to the server.js file
const frontEndAppBuildPath = path.resolve(__dirname, './build');
server.app.use(serve(frontEndAppBuildPath))

server.run(PORT, () => {
  server.app.use(
    async (ctx, next) => await serve(frontEndAppBuildPath)(
      Object.assign(ctx, { path: 'index.html' }),
      next
    )
  )
});
The Lobby might be as follows:

import React from 'react';
import { Lobby } from 'boardgame.io/react';
import { TicTacToeBoard } from './board';
import { TicTacToe } from './game';

const { protocol, hostname, port } = window.location;
const server = `${protocol}//${hostname}:${port}`;
const importedGames = [{ game: TicTacToe, board: TicTacToeBoard }];

export default () => (
  <div>
    <h1>Lobby</h1>
    <Lobby gameServer={server} lobbyServer={server} gameComponents={importedGames} />
  </div>
);
Or, without the lobby, pass the server address when calling SocketIO:

import { SocketIO } from 'boardgame.io/multiplayer';

const { protocol, hostname, port } = window.location;
const server = `${protocol}//${hostname}:${port}`;

const GameClient = Client({
  // ...
  multiplayer: SocketIO({ server }),
});
Backend Only
If you only need to publish your backend to Heroku, your server.js can be simplified to this:

// server.js

import { Server } from 'boardgame.io/server';
import { TicTacToe } from './game';

const server = Server({ games: [TicTacToe] });
const PORT = process.env.PORT || 8000;

server.run(PORT);
And your Lobby would now be pointing to your Heroku app URL:

import React from 'react';
import { Lobby } from 'boardgame.io/react';
import { TicTacToeBoard } from './board';
import { TicTacToe } from './game';

const server = `https://yourapplication.herokuapp.com`;
const importedGames = [{ game: TicTacToe, board: TicTacToeBoard }];

export default () => (
  <div>
    <h1>Lobby</h1>
    <Lobby gameServer={server} lobbyServer={server} gameComponents={importedGames} />
  </div>
);
Or, without the lobby, pass the Heroku app URL when calling SocketIO:

import { SocketIO } from 'boardgame.io/multiplayer';

const GameClient = Client({
  // ...
  multiplayer: SocketIO({ server: 'https://yourapplication.herokuapp.com' }),
});



Storage
boardgame.io is storage agnostic. Various adapters are available that allow you to persist your game state in different storage systems.

You can even write your own adapter for a custom backend.

Flatfile
First, install the necessary packages:

npm install node-persist
Then modify your server spec to indicate that you want to connect to a flatfile database:

const { Server, FlatFile } = require('boardgame.io/server');
const { TicTacToe } = require('./game');

const server = Server({
  games: [TicTacToe],

  db: new FlatFile({
    dir: '/storage/directory',
    logging: (true/false),
    ttl: (optional, see node-persist docs),
  }),
});

server.run(8000);
Other backends
Firebase
Instructions at https://github.com/delucis/bgio-firebase.

Azure Storage
Instructions at https://github.com/c-w/bgio-azure-storage.

Postgres
Instructions at https://github.com/janKir/bgio-postgres.

MongoDB
Coming soon (used to be supported but is not in sync with the latest release).

Caching
Depending on your set-up, you may want the server to cache some of the data, reducing the load on your database and speeding up server responses. @boardgame.io/storage-cache offers a basic caching model compatible with any boardgame.io database connector.

Writing a Custom Adapter
Create a class that implements the StorageAPI.Async interface.


Chat
The boardgame.io client provides a basic API for sending chat messages between players in a match using the multiplayer server.

The plain JS client and the React client (via board props) both provide the following properties:

sendChatMessage(message): Function that sends a chat message to other players. The message argument can be a string or you can send objects to include more metadata. For example, you might decide to include a timestamp along with message text:

sendChatMessage({ message: 'Hello', time: Date.now() });
chatMessages: An array containing chat messages this client has received. Each message is an object with the following properties:

id: a unique message ID string
sender: the playerID of the message’s sender
payload: the value of the message argument passed to sendChatMessage
Example chatMessages array:

[
    { id: 'foo', sender: '0', payload: 'Ready to play?' },
    { id: 'bar', sender: '1', payload: 'Let’s go!' },
]
Notes
Chat messages are ephemeral and are not stored by the boardgame.io server. A client only receives messages sent while it is connected to the server. If messages are sent amongst players before another player has connected, the new player will not receive those prior messages. Similarly, if the page is refreshed, any previously received messages will be lost.

Only players can send chat messages. Assuming the match is authenticated via the Lobby server, only players are permitted to send messages, which are authenticated using the same logic as other game actions. Spectator clients can receive and view chat messages, but not send messages of their own.



TypeScript
boardgame.io includes type definitions for TypeScript.

Basic usage
// Game.ts
import type { Game, Move } from "boardgame.io";

export interface MyGameState {
  // aka 'G', your game's state
}

const move: Move<MyGameState> = ({ G, ctx }) => {};

export const MyGame: Game<MyGameState> = {
  // ...
};
Open this snippet in the TypeScript Playground ↗︎

React
React components must include boardgame.io-specific properties, so extend your props from BoardProps. By passing your game state type to BoardProps, you’ll get the correct typing for G in your board component.

// Board.tsx
import type { BoardProps } from 'boardgame.io/react';
import type { MyGameState } from './Game.ts'

interface MyGameProps extends BoardProps<MyGameState> {
  // Additional custom properties for your component
}

export function MyGameBoard(props: MyGameProps) {
  // Your game board
}
Read more about Client in the reference. No special typing should be required.

// App.tsx
import { Client } from 'boardgame.io/react';

import { MyGame } from './Game';
import { MyGameBoard } from './Board';

const App = Client({
  game: MyGame,
  board: MyGameBoard,
});
export default App;

