phaser-jsx
    Preparing search index...

    phaser-jsx

    phaser-jsx

    NPM

    NPM version build codecov

    📝 Use JSX in Phaser.

    Examples | StackBlitz | CodeSandbox | JSFiddle

    With JSX:

    // index.jsx
    import Phaser from 'phaser';
    import { Text, render } from 'phaser-jsx';

    new Phaser.Game({
    scene: {
    create() {
    render(<Text text="Hello, world!" />, this);
    },
    },
    });

    Without JSX:

    // index.js
    import Phaser from 'phaser';
    import { jsx, render } from 'phaser-jsx';

    new Phaser.Game({
    scene: {
    create() {
    render(jsx(Phaser.GameObjects.Text, { text: 'Hello, world!' }), this);
    },
    },
    });

    NPM:

    npm install phaser-jsx
    

    Yarn:

    yarn add phaser-jsx
    

    CDN:

    <script src="https://unpkg.com/phaser@latest/dist/phaser.min.js"></script>
    <script src="https://unpkg.com/phaser-jsx@latest/umd/phaser-jsx.min.js"></script>

    ES Modules:

    import { createElement, render } from 'phaser-jsx';
    

    CommonJS:

    const { createElement, render } = require('phaser-jsx');
    

    UMD:

    <script src="https://unpkg.com/phaser@latest/dist/phaser.min.js"></script>
    <script src="https://unpkg.com/phaser-jsx@latest/umd/phaser-jsx.min.js"></script>
    <script>
    const { render, jsx } = window.PhaserJSX;
    </script>

    For better type support, install @types/react:

    npm install @types/react --save-dev
    

    Import the GameObject from phaser-jsx instead of phaser:

    import { Text } from 'phaser-jsx';
    
    Tip

    All GameObjects exported from phaser-jsx are aliases of the GameObjects from phaser.

    Update your tsconfig.json:

    {
    "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "phaser-jsx"
    }
    }

    Update your Vite config:

    // vite.config.mjs
    import { defineConfig } from 'vite';

    export default defineConfig({
    esbuild: {
    jsxImportSource: 'phaser-jsx',
    },
    });

    If you're not using jsxImportSource, you can set a JSX pragma at the top of your file:

    /** @jsx jsx */
    import { jsx } from 'phaser-jsx';

    This package follows React conventions like having createElement and jsx-runtime.

    The render function renders game objects inside a scene.

    If you need nesting and relative positioning, use Container:

    <Container>
    <Text text="Child 1" />
    <Text text="Child 2" />
    </Container>

    Use Fragment to render children without a parent:

    import { Fragment } from 'phaser-jsx';

    <Fragment>
    <Text text="Item 1" />
    <Text text="Item 2" />
    </Fragment>;
    Note

    The shorthand syntax <></> doesn't work here.

    Create a ref for your game object:

    import { useRef } from 'phaser-jsx';

    function MyComponent() {
    const textRef = useRef<Phaser.GameObjects.Text>();
    // ...
    }

    Pass your ref to the JSX element:

    // ...
    return <Text ref={textRef} onPointerDown={handleClick} />;

    Access your game object:

    function handleClick() {
    textRef.current?.text = 'Clicked';
    }

    Alternatively, you can get the game object with a callback ref:

    <Text
    ref={(gameObject) => {
    gameObject.text = 'Hello, world!';
    }}
    />

    Retrieve the current Scene with the useScene hook:

    import { useScene } from 'phaser-jsx';

    function MyComponent() {
    const scene = useScene();
    // ...
    }
    Warning

    Don't use the useScene hook if you start multiple Scenes.

    Type your Scene with TypeScript:

    class MyScene extends Phaser.Scene {
    // ...
    }

    const scene = useScene<MyScene>();

    Manage state that triggers a re-render of your game object:

    import { useState } from 'phaser-jsx';

    function ScoreDisplay() {
    const [score, setScore] = useState(0);

    return (
    <Container>
    <Text text={`Score: ${score}`} />
    <Text
    text="Add Point"
    onPointerDown={() => setScore(score + 1)}
    />
    </Container>
    );
    }

    The setter also accepts a function updater:

    setScore((prev) => prev + 1);
    

    Run a side effect after render:

    import { useEffect } from 'phaser-jsx';

    function MyComponent() {
    useEffect(() => {
    console.log('mounted');
    }, []);
    }

    The second argument is a dependency array. The effect re-runs whenever a dependency changes:

    const [score, setScore] = useState(0);

    useEffect(() => {
    console.log('score:', score);
    }, [score]);

    Omit the dependency array to run the effect after every render:

    useEffect(() => {
    console.log('rendered');
    });

    Return a cleanup function to run before the next effect or on unmount:

    useEffect(() => {
    const listener = scene.input.on('pointerdown', handleClick);
    return () => listener.destroy();
    }, []);

    Release is automated with Release Please.

    MIT