Using Pixi.js in React with ReactPixi
Integrating the high-performance 2D rendering engine Pixi.js with
React’s declarative UI can be challenging because Pixi.js relies on an
imperative canvas API, while React manages a virtual DOM. This article
explains how to bridge this gap using the official wrapper library
@pixi/react (formerly ReactPixi). You will learn how to set
up a PixiJS stage within a React application, handle state updates,
create interactive graphics, and apply performance optimization
techniques to build seamless 2D web experiences.
Why Use ReactPixi?
Directly manipulating a Pixi.js canvas inside a React component using
standard React hooks (like useEffect) can quickly lead to
messy, unmaintainable code. @pixi/react solves this by
providing custom React fiber reconcilers. This allows you to write
PixiJS code declaratively using JSX tags such as
<Stage />, <Container />, and
<Sprite />.
By using ReactPixi, any changes in your React state automatically propagate to your PixiJS elements, giving you the best of both worlds: React’s state management and PixiJS’s WebGL rendering speeds.
Setting Up Your First ReactPixi Application
To get started, you need to install the required packages. Run the following command in your terminal:
npm install pixi.js @pixi/reactOnce installed, you can render a basic PixiJS application using React
components. The following code demonstrates how to set up a viewport
(Stage) and render a simple graphic:
import React from 'react';
import { Stage, Graphics } from '@pixi/react';
const MyPixiApp = () => {
const drawCircle = (g) => {
g.clear();
g.beginFill(0xde3249);
g.drawCircle(150, 150, 50);
g.endFill();
};
return (
<Stage width={300} height={300} options={{ backgroundColor: 0x1099bb }}>
<Graphics draw={drawCircle} />
</Stage>
);
};
export default MyPixiApp;The <Stage /> component creates the canvas element
and initializes the PixiJS Application instance. The
<Graphics /> component is used to draw vector shapes
inside that canvas.
Managing State and Interaction
To make your canvas interactive, you can bind React state to PixiJS component properties. When the React state changes, the wrapper library efficiently updates only the changed properties of the underlying PixiJS objects.
import React, { useState } from 'react';
import { Stage, Sprite } from '@pixi/react';
const InteractiveCanvas = () => {
const [position, setPosition] = useState({ x: 100, y: 100 });
const moveSprite = () => {
setPosition({
x: Math.random() * 400,
y: Math.random() * 400,
});
};
return (
<Stage width={500} height={500} options={{ backgroundColor: 0x1099bb }}>
<Sprite
image="https://pixijs.com/assets/bunny.png"
x={position.x}
y={position.y}
interactive={true}
pointerdown={moveSprite}
anchor={0.5}
/>
</Stage>
);
};In this example, clicking the bunny sprite triggers the
moveSprite function. This updates the React state, which
immediately updates the x and y props of the
<Sprite /> component, causing the bunny to move on
the screen.
Accessing the Raw PixiJS Instance
There are scenarios where you need direct access to the underlying
PixiJS objects—such as setting up complex filters, shaders, or accessing
the PixiJS Ticker. You can achieve this using the useApp
hook provided by @pixi/react.
import React, { useEffect } from 'react';
import { Stage, useApp } from '@pixi/react';
const TickerComponent = () => {
const app = useApp();
useEffect(() => {
const tick = (delta) => {
// Custom game loop logic using the raw PixiJS app instance
};
app.ticker.add(tick);
return () => app.ticker.remove(tick);
}, [app]);
return null;
};
// Usage
const App = () => (
<Stage>
<TickerComponent />
</Stage>
);Note: Custom hooks like useApp can only be used in
components that are children of the <Stage />
component, as they rely on React Context.
Best Practices for Performance
While @pixi/react makes integration easy, combining
React’s virtual DOM reconciliation with WebGL can sometimes introduce
performance bottlenecks. Use these strategies to keep your application
running at 60 FPS:
- Minimize React Re-renders: Avoid updating React
state every single frame. For animations (like rotations or movement
physics), use the PixiJS ticker directly via
useAppor custom callback refs rather than updating state variables on every frame. - Reuse Textures: Loading images repeatedly degrades
performance. Preload your assets using Pixi’s
Assetsclass and pass the cached textures to your<Sprite />components instead of passing raw image URLs. - Batch Renderers: Group static elements inside
<Container />components to allow PixiJS to batch draw calls efficiently.