ReactPixiFiber is a JavaScript library for writing PixiJS applications using React declarative style in React 16 and above.
For React <16.0.0 see react-pixi.
See Rotating Bunny demo.
Also, please explore our CodeSandbox templates:
and examples:
- Rotating Bunny
- AnimatedSprite using CustomComponent
- Aligning texts
- Sharing Redux state
- Using animated
Read migration guide.
The current version assumes React >16.0.0 and PixiJS >4.4.0
yarn add react-pixi-fiber prop-types pixi.js
or
npm install react-pixi-fiber prop-types pixi.js --save
Refer to next sections to see usage examples.
This package works flawlessly with Create React App โ see examples below, they already use it.
With ReactDOM (React 18 and above)
import { createRoot } from "react-dom/client";
import { Sprite, Stage } from "react-pixi-fiber";
import bunny from "./bunny.png";
function Bunny (props) {
return <Sprite texture={PIXI.Texture.from(bunny)} {...props} />;
}
const container = document.getElementById("container");
const root = createRoot(container);
root.render(
<Stage options={{ backgroundColor: 0x10bb99, height: 600, width: 800 }}>
<Bunny x={200} y={200} />
</Stage>,
);
This example will render PIXI.Sprite
object into a Root Container of PIXI.Application
on the page.
The HTML-like syntax; called JSX is not required to use with this renderer, but it makes code more readable. You can use Babel with a React preset to convert JSX into native JavaScript.
With ReactDOM (React 16 and 17)
import { render } from "react-dom";
import { Sprite, Stage } from "react-pixi-fiber";
import bunny from "./bunny.png";
function Bunny(props) {
return <Sprite texture={PIXI.Texture.from(bunny)} {...props} />;
}
const container = document.getElementById("container");
render(
<Stage options={{ backgroundColor: 0x10bb99, height: 600, width: 800 }}>
<Bunny x={200} y={200} />
</Stage>,
container
);
This example will render PIXI.Sprite
object into a Root Container of PIXI.Application
on the page.
The HTML-like syntax; called JSX is not required to use with this renderer, but it makes code more readable. You can use Babel with a React preset to convert JSX into native JavaScript.
Without ReactDOM
import { render, Text } from "react-pixi-fiber";
import * as PIXI from "pixi.js";
// Setup PixiJS Application
const canvasElement = document.getElementById("container")
const app = new PIXI.Application({
backgroundColor: 0x10bb99,
view: canvasElement,
width: 800,
height: 600,
});
render(
<Text text="Hello World!" x={200} y={200} />,
app.stage
);
This example will render PIXI.Text
object into a Root Container of PIXI Application (created as app
) inside the <canvas id="container"></canvas>
element on the page.
- Run
yarn install
(ornpm install
) in the repository root. - Run
yarn install
(ornpm install
) in theexamples
directory. - Run
yarn start
(ornpm run start
) in theexamples
directory. - Wait few seconds and browse examples that will open in new browser window.
Migrating from [email protected]
(before version 1.0.0
)
Changed built-in Stage
and the one returned by createStageClass()
to have the same API
It is now possible to get ref
to built-in Stage
.
Unless you are using class-based Stage
component explicitly in your application, for example you are extending it, you should prefer to use built-in Stage
instead of creating it with createStageClass()
.
Data available on the Stage
"instance":
_app
- PIXI.Application instance_canvas
- HTMLCanvasElement instanceprops
- props passed to Stage component
For example:
import * as React from "react";
import { Stage, Text } from "react-pixi-fiber";
const width = 600;
const height = 400;
const options = {
backgroundColor: 0x56789a,
width: width,
height: height
};
const style = {
width: width,
height: height
};
function App() {
const stageRef = React.useRef()
React.useEffect(() => {
// Access PIXI.Application instance
console.log(stageRef.current?._app.current)
// Access HTMLCanvasElement instance
console.log(stageRef.current?._canvas.current)
// Access props passed to Stage component
console.log(stageRef.current?.props)
}, [])
return (
<Stage options={options} style={style} ref={stageRef}>
<Text x={100} y={100} text="Hello world!" />
</Stage>
);
}
Changed PIXI.Application
exposed by Stage
to be React ref
This is only relevant if you were using createStageClass()
to create Stage
component, as it was impossible to get ref
when using built-in Stage
as if was a function component, which triggered Warning: Function components cannot be given refs
error.
For example:
import * as React from "react";
import { createStageClass, Text } from "react-pixi-fiber";
const Stage = createStageClass()
const width = 600;
const height = 400;
const options = {
backgroundColor: 0x56789a,
width: width,
height: height
};
const style = {
width: width,
height: height
};
function App() {
const stageRef = React.useRef()
React.useEffect(() => {
- console.log(stageRef.current?._app.renderer)
+ console.log(stageRef.current?._app.current.renderer)
}, [])
return (
<Stage options={options} style={style} ref={stageRef}>
<Text x={100} y={100} text="Hello world!" />
</Stage>
);
}
Changed oldProps
in customApplyProps
to not be initialised when the component is first rendered
Make sure to check if oldProps
is initialised before trying to read properties from it.
For example:
import { Container, CustomPIXIComponent } from "react-pixi-fiber"
const TYPE = "CustomContainer"
const behavior = {
customApplyProps: function (instance, oldProps, newProps) {
- const { customProp: oldCustomProp, ...otherOldProps } = oldProps
+ const { customProp: oldCustomProp, ...otherOldProps } = oldProps ?? {}
const { customProp, ...otherNewProps } = newProps
if (customProp !== oldCustomProp) {
// Do something when customProp value have changed
}
this.applyDisplayObjectProps(otherOldProps, otherNewProps)
},
customDisplayObject: function ({ customProp, ...props }) {
const container = new PIXI.Container(props)
if (customProp === "foo") {
// Do something when customProp is equal to "foo"
}
return container
},
}
export default CustomPIXIComponent(behavior, TYPE)
Changed applyProps
to applyDisplayObjectProps
react-pixi-fiber
now needs to know the type of component (e.g. "Sprite"
) to properly apply the props.
For example:
-import { applyProps } from "react-pixi-fiber"
+import { applyDisplayObjectProps } from "react-pixi-fiber"
function ApplyAnimatedValues(instance, props) {
if (instance instanceof PIXI.DisplayObject) {
- applyProps(instance, {}, props)
+ // Component has custom way of applying props - use that
+ if (typeof instance._customApplyProps === "function") {
+ instance._customApplyProps(instance, {}, props)
+ }
+ // Component doesn't have custom way of applying props - use default way
+ else {
+ const type = instance.constructor.name
+ applyDisplayObjectProps(type, instance, {}, props)
}
} else {
return false
}
}
Refer to the implementation, when in doubt:
- old
applyProps
->react-pixi-fiber/src/ReactPixiFiber.js
Line 43 in 64e8e9f
- new
applyDisplayObjectProps
->
It is possible to use React Pixi Fiber as a drop-in replacement for react-pixi
.
There are two options:
Changing import
or require
statements
Change:
import ReactPIXI from "react-pixi";
// or
const ReactPIXI = require("react-pixi");
to:
import ReactPIXI from "react-pixi-fiber/react-pixi-alias";
// or
const ReactPIXI = require("react-pixi-fiber/react-pixi-alias");
Using webpack
resolve alias
resolve: {
alias: {
"react-pixi$": "react-pixi-fiber/react-pixi-alias"
}
}
React Pixi Fiber currently supports following components out of the box (but read Custom Components section if you need more):
Renders Root Container of any PIXI.Application
.
Expects one the following props:
app
- pass your ownPIXI.Application
instance,options
- pass only thePIXI.Application
options.
Renders PIXI.Container
.
Renders PIXI.Graphics
.
Renders PIXI.ParticleContainer
(or PIXI.particles.ParticleContainer
if you're using PixiJS 4).
Renders PIXI.Sprite
.
Renders PIXI.TilingSprite
(or PIXI.extras.TilingSprite
if you're using PixiJS 4).
Renders PIXI.Text
.
Renders PIXI.BitmapText
(or PIXI.extras.BitmapText
if you're using PixiJS 4).
Renders PIXI.NineSlicePlane
.
Similarly to ReactDOM in React 16,
ReactPixiFiber is not ignoring unknown PIXI.DisplayObject
members โ they are all passed through. You can read
more about Unknown Prop Warning in ReactDOM.
In case you are using PixiJS plugins, such as pixi-layers
, ReactPixiFiber can
recognize these custom props by using the following CustomPIXIProperty
API:
CustomPIXIProperty(maybeComponentType, propertyName, validator)
accepts:
maybeComponentType
โ a ReactPixiFiber component, an array of ReactPixiFiber components orundefined
/null
. Passingundefined
ornull
will apply custom property to all ReactPixiFiber components.propertyName
โ a name of the custom property as string. ReactPixiFiber will also check that the casing is correct.validator
โ optional function that will be called with value provided and should returntrue
if the value is valid,false
otherwise.
For example:
import { Container, Sprite } from "react-pixi-fiber";
const group = new PIXI.display.Group(0, true);
// if you just want to get rid of Unknown Prop Warning:
CustomPIXIProperty(Container, "parentGroup");
CustomPIXIProperty(undefined, "zIndex");
// if you want to be strict in the values that are provided
CustomPIXIProperty(Container, "parentGroup", value => value instanceof PIXI.display.Group);
CustomPIXIProperty([Container, Sprite], "zIndex", value => Number.isFinite(value));
function App() {
return (
<Container>
<Container parentGroup={group}>
<Sprite texture={PIXI.Texture.WHITE} x={10} y={10} zIndex={1} />
<Sprite texture={PIXI.Texture.WHITE} x={15} y={15} zIndex={2} />
</Container>
{/* `parentgroup` below will trigger prop warning, as the letter casing is incorrect */}
<Container parentgroup={group}>
<Sprite texture={PIXI.Texture.WHITE} x={100} y={100} zIndex={1} />
{/* `zindex` below will trigger prop warning, as the letter casing is incorrect */}
<Sprite texture={PIXI.Texture.WHITE} x={105} y={105} zindex={2} />
</Container>
</Container>
)
}
For setting properties on PixiJS types that are either PIXI.Point
s or PIXI.ObservablePoint
s you can use either
and array of integers or a comma-separated string of integers in the following forms: [x,y]
, "x,y"
, [i]
, "i"
.
In the case where two integers are provided, the first will be applied to the x
coordinate and the second will be
applied to the y
coordinate. In the case where a single integer if provided, it will be applied to both coordinates.
You can still create your own PIXI Point
or ObservablePoint
objects and assign them directly to the property.
These won't actually replace the property but they will be applied using the original object's .copy()
method.
PIXI.Application
is automatically provided using the following definition (either as a prop or in context):
app
โ an instance of PixiJS Application, with properties like:loader
โ Loader instance to help with asset loading,renderer
โ WebGL or CanvasRenderer,ticker
โ Ticker for doing render updates,view
โ reference to the renderer's canvas element.
Using withApp
Higher-Order Component (with all React versions)
To get app
prop in your component you may wrap it with withApp
higher-order component:
import { render } from "react-dom";
import { Sprite, Stage, withApp } from "react-pixi-fiber";
import bunny from "./bunny.png";
class RotatingBunny extends Component {
state = {
rotation: 0,
};
componentDidMount() {
// Note that `app` prop is coming through `withApp` HoC
this.props.app.ticker.add(this.animate);
}
componentWillUnmount() {
this.props.app.ticker.remove(this.animate);
}
animate = delta => {
this.setState(state => ({
rotation: state.rotation + 0.1 * delta,
}));
};
render() {
return (
<Sprite
{...this.props}
texture={PIXI.Texture.from(bunny)}
rotation={this.state.rotation}
/>
);
}
}
RotatingBunny.propTypes = {
app: PropTypes.object.isRequired,
};
const RotatingBunnyWithApp = withApp(RotatingBunny);
render(
<Stage options={{ backgroundColor: 0x10bb99, height: 600, width: 800 }}>
<RotatingBunnyWithApp x={200} y={200} />
</Stage>,
document.getElementById("container")
);
Using New Context API directly (with React 16.3.0 and newer)
import { render } from "react-dom";
import { AppContext, Sprite, Stage } from "react-pixi-fiber";
import bunny from "./bunny.png";
class RotatingBunny extends Component {
state = {
rotation: 0,
};
componentDidMount() {
// Note that `app` prop is coming directly from AppContext.Consumer
this.props.app.ticker.add(this.animate);
}
componentWillUnmount() {
this.props.app.ticker.remove(this.animate);
}
animate = delta => {
this.setState(state => ({
rotation: state.rotation + 0.1 * delta,
}));
};
render() {
return (
<Sprite
{...this.props}
texture={PIXI.Texture.from(bunny)}
rotation={this.state.rotation}
/>
);
}
}
RotatingBunny.propTypes = {
app: PropTypes.object.isRequired,
};
render(
<Stage options={{ backgroundColor: 0x10bb99, height: 600, width: 800 }}>
<AppContext.Consumer>
{app => (
<RotatingBunny app={app} x={200} y={200} />
)}
</AppContext.Consumer>
</Stage>,
document.getElementById("container")
);
Using Legacy Context API directly (with React older than 16.3.0)
This approach is not recommended as it is easier to just use withApp
HoC mentioned above.
import { render } from "react-dom";
import { Sprite, Stage } from "react-pixi-fiber";
import bunny from "./bunny.png";
class RotatingBunny extends Component {
state = {
rotation: 0,
};
componentDidMount() {
// Note that `app` is coming from context, NOT from props
this.context.app.ticker.add(this.animate);
}
componentWillUnmount() {
this.context.app.ticker.remove(this.animate);
}
animate = delta => {
this.setState(state => ({
rotation: state.rotation + 0.1 * delta,
}));
};
render() {
return (
<Sprite
{...this.props}
texture={PIXI.Texture.from(bunny)}
rotation={this.state.rotation}
/>
);
}
}
// Note that here we tell React to apply `app` via legacy Context API
RotatingBunny.childContextTypes = {
app: PropTypes.object,
};
render(
<Stage options={{ backgroundColor: 0x10bb99, height: 600, width: 800 }}>
<RotatingBunny x={200} y={200} />
</Stage>,
document.getElementById("container")
);
ReactPixiFiber can recognize your custom components using API compatible with react-pixi
.
CustomPIXIComponent(behavior, type)
accepts a behavior
object with the following 4 properties and a type
string.
Use this to create an instance of [PIXI.DisplayObject].
This is your entry point to custom components and the only required method. Can be also passed as behavior
of type function
to CustomPIXIComponent
.
Use this to apply newProps
to your Component
in a custom way.
Note: this replaces the default method of transfering props
to the specified displayObject
. Call this.applyDisplayObjectProps(oldProps,newProps)
inside your customApplyProps
method if you want that.
Use this to do something after displayObject
is attached, which happens after componentDidMount
lifecycle method.
Use this to do something (usually cleanup) before detaching, which happens before componentWillUnmount
lifecycle method.
For example, this is how you could implement Rectangle
component:
// components/Rectangle.js
import { CustomPIXIComponent } from "react-pixi-fiber";
import * as PIXI from "pixi.js";
const TYPE = "Rectangle";
export const behavior = {
customDisplayObject: props => new PIXI.Graphics(),
customApplyProps: function(instance, oldProps, newProps) {
const { fill, x, y, width, height } = newProps;
instance.clear();
instance.beginFill(fill);
instance.drawRect(x, y, width, height);
instance.endFill();
}
};
export default CustomPIXIComponent(behavior, TYPE);
// App.js
import { render } from "react-pixi-fiber";
import * as PIXI from "pixi.js";
import Rectangle from "./components/Rectangle"
// Setup PixiJS Application
const canvasElement = document.getElementById("container")
const app = new PIXI.Application(800, 600, {
view: canvasElement
});
render(
<Rectangle
x={250}
y={200}
width={300}
height={200}
fill={0xFFFF00}
/>,
app.stage
);
Yes and it's awesome! It is battle tested and backed up by Kalamba Games since the conception in the beginning of 2018 (after migrating from react-pixi
) and now also used by other game studios.
PixiJS v4, v5 and v6 are supported.
Sure thing! We've got you covered.
Can I use already existing PIXI.Application
?
Yes, you can pass app
property to Stage
component, e.g. <Stage app={app} />
.
Yes, it is easy, read migration guide.
No, unfortunately it is not supported right now.
The main purpose of this repository is to be able to render PixiJS objects inside React 16 Fiber architecture.
Development of React Pixi Fiber happens in the open on GitHub, and I would be grateful to the community for any contributions, including bug reports and suggestions.
Read below to learn how you can take part in improving React Pixi Fiber.
React Pixi Fiber has adopted a Contributor Covenant Code of Conduct that we expect project participants to adhere to. Please read the full text so that you can understand what actions will and will not be tolerated.
Read the contributing guide to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to React Pixi Fiber.
You can help others and discuss in our gitter channel.
ReactPixiFiber is MIT licensed.
For making PIXI available in React for the first time.
For deeply explaining the concepts of Fiber architecture.
For helping me understand how to build an actual renderer.
On which this renderer was initially based.
React Contributors
For making an awesome project structure and documentation that is used in similar fashon here.