How to integrate Phaser into React

The benefits and how to communicate between Phaser and React/Redux

Leo Kuo
6 min readJul 2, 2023

When I was trying to create an HTML5 game, I encountered some challenges regarding whether to use React and how to integrate a game engine. Eventually, I decided to integrate the Phaser game engine into React, and the outcome was quite satisfactory. In this article, I will explain why I chose to integrate Phaser into React and provide a step-by-step implementation guide from my perspective.

The disadvantages of using React and Phaser for game development

When it comes to creating an HTML5 game, it is not recommended to use React directly for game development. React was not specifically designed for game development, and it has certain limitations in this context. One of the advantages of React is its responsiveness, but its design logic based on functional programming can make it challenging to implement certain game scenarios.

For example, let’s consider the task of moving a game object from one position to another on the screen. In React, we would need to store the state of the object and use a hook to handle the animation and response. However, when scaling this to multiple objects, maintaining the states and code can become increasingly difficult. Managing the states and keeping track of code logic for a large number of objects can quickly become cumbersome.

In summary, the disadvantages of using React for game development include:

  • Functional programming limitations: React’s design philosophy, which emphasizes functional programming, may not align well with the requirements of game development. Games often involve complex interactions, animations, and real-time updates, which can be more challenging to implement using React’s functional programming approach.
  • Lack of game-specific tools: React does not provide out-of-the-box tools and features specifically tailored for game development. Game development typically requires functionalities like physics engines, sprite management, collision detection, and game loop management, which are not natively available in React.
  • Performance concerns: While React is known for its efficient rendering and virtual DOM updates, game development requires highly optimized performance to deliver smooth gameplay experiences. React’s reconciliation algorithm and virtual DOM may introduce overhead that could impact game performance, especially in scenarios with intensive animations and real-time updates.
  • State management complexities: React provides mechanisms like state and hooks, managing game state across multiple components and handling complex game logic can become more challenging and less intuitive compared to using a dedicated game engine.

Regarding Phaser, it is indeed a popular 2D game framework for creating HTML5 games. It utilizes both the Canvas and WebGL renderers to render HTML canvas in web browsers. While it is possible to directly use Phaser to develop an HTML5 game, there are some disadvantages to consider:

  • Inconvenience in creating game UI: In Phaser, creating a game UI involves creating individual game objects and specifying their positions. This process can be more cumbersome compared to using UI frameworks specifically designed for building interfaces. Additionally, the fidelity of UI elements in Phaser is limited to pixels rather than vector-based graphics, which can result in lower visual quality.
  • Hard to mange high level game states: For example, if we want to make a baseball game. We properly need to store some data like teams information, players information and even in-game records. It’s hard to manage all data in Phaser.

What are the benefits that integrate Phaser into React?

Integrating Phaser into React offers several benefits that can help overcome the limitations of both frameworks individually. Here are some advantages of combining Phaser and React:

  • Leveraging Phaser’s game engine features: You can utilize the powerful game development capabilities provided by Phaser. This includes features such as scene management, game object creation, physics engines, input handling, and animation control. Phaser’s well-designed and optimized features can greatly enhance the game development process.
  • Utilizing React’s UI rendering capabilities: By using React for UI components and rendering, you can take advantage of its virtual DOM and component-based architecture. This allows for fast and efficient UI updates.
  • Effective data management with React: You can use React to handle high-level game states, such as player scores, game progress, and in-game variables. React’s data management capabilities ensure that changes in game data are efficiently.

How to integrate Phaser into React

We need to build a basic React app firstly. In this article, I will skip this part and focusing on how to combine Phaser and React.

After create a react app, run yarn add phaser to add Phaser package.

Then, add a div in index.html file. Phaser will render a canvas into the div later.

<body>
<div id="root"></div>
<!-- add the following line -->
<div id="phaser-container"></div>
</body>

Set Phaser game config.

const config: Phaser.Types.Core.GameConfig = {
type: Phaser.AUTO,
parent: 'phaser-container', // exactly equal to the div id in html
scale: {
mode: Phaser.Scale.ScaleModes.RESIZE,
width: window.innerWidth,
height: window.innerHeight,
},
scene: [GameScene], // put scences into the array
};

How to communicate between Phaser and React

In this part, I used Redux to manage states. Let’s install @reduxjs/toolkit and react-redux packages firstly.

yarn add @reduxjs/toolkit react-redux

The core concept of communication between Phaser and React is through the use of a Redux store. By storing states that need to be accessed and modified by both frameworks in the Redux state, we establish a shared data source.

On the Phaser side, we can use the dispatch function to mutate the shared states. When there is a need to modify the state from within Phaser, we can dispatch Redux actions, which in turn update the shared state and trigger the necessary updates in the React components.

import { createSlice, PayloadAction } from '@reduxjs/toolkit';

const gameInfoSlice = createSlice({
name: 'gameInfoSlice',
initialState: {
pitchingInfo: {
isPitching: false, // define if the pitcher is pitching currently
handedness: 'R', // define the handedness of the pitcher
}
},
reducer: {
setIsPitching: (state, action: PayloadAction<{ isPitching: boolean }>) => {
const { isPitching } = action.payload;
state.pitchingInfo.isPitching = isPitching;
},
setPitchHandedness: (state, action: PayloadAction<{ handedness: 'R' | 'L' }>) => {
const { handedness } = action.payload;
state.pitchingInfo.handedness = handedness;
},
}
});

export { setIsPitching, setPitchHandedness } = gameInfoSlice.actions
export const gameInfoReducer = gameInfoSlice.reducer;

Configure and export store:

import { configureStore } from '@reduxjs/toolkit';

import { gameInfoReducer } from './slices/game-info';


export const store = configureStore({
reducer: {
gameInfo: gameInfoReducer,
},
});

Scenario A: Redux state changed -> Phaser need to be responsive

For instance, we substitute pitcher from right handedness to left handedness in game UI. Phaser should make pitcher object change from right-handed to left-handed.

// Just an example for getting pitcher data by id
const currentPitcherData = useMemo(() => {
if (!currentPitcherId) return undefined;
return getDataById(currentPitcherId);
}, [currentPitcherId, getDataById]);

// Pitcher handedness
useEffect(() => {
if (!currentPitcherData) return;
dispatch(setPitchHandedness({ handedness: currentPitcherData.handedness.pitching })); // 'R' | 'L'
}, [currentPitcherData, dispatch]);

From Phaser side, we subscribe state change event in a scene. Therefore, we could know when we need to modify the game object!

// GameScene
export default class GameScene extends Phaser.Scene {
public pitcher: PitcherSprite; // for example we have a custom pitcher object

constructor() {
super('game');
this.pitcher = new PitcherSprite();
}

create() {
// listen for state changes, TODO: it's better to deal with unsubscribe after shutdown the scene
store.subscribe(this.stateUpdate);
}

stateUpdate() {
const newState = store.getState();
const pitchHandedness = newState.gameInfo.pitchingInfo.handedness;
this.pitcher.setHandedness(pitchHandedness); // set handedness after state updated
}

}

Scenario B: Phaser modify a redux state

By calling store.dispatch($action) :

import { setIsPitching } from '../store/slices/game-info';

export class PitcherSprite extends Phaser.GameObjects.Sprite {

// just an example
constructor(scene: Phaser.Scene, atlasKey: string) {
super(scene, 0, 0, atlasKey);

this.anims.create({
key: 'start_pitching',
frames: this.anims.generateFrameNames(this.texture.key, {
prefix: 'pitcher',
suffix: '.png',
start: 0,
end: 34,
zeroPad: 5,
}),
repeat: 0,
frameRate: 24,
});
}

startPitching() {
this.play('start_pitching');
store.dispatch(setIsPitching({ isPitching: true })); // notify React that is pitching now
}

}

Demo and conclusion

By taking advantage of the strengths of both React and Phaser, we can achieve a smoother game development workflow. React handles UI rendering and state management, while Phaser focuses on game mechanics and rendering. This integration allows for efficient collaboration between the two frameworks, resulting in a more streamlined and powerful game development experience.

--

--

Leo Kuo

Indie Game Developer | Frontend Developer | iOS Developer | UIUX