Babylon.js and React
How to use Babylon.js with React
For anyone interested, it is not difficult to integrate Babylon.js into a React application.
What you need
It's up to you whether you choose Create React App or a custom project; this guide only discusses how to load Babylon.js in a component. This example uses the newer @babylonjs/core ES6 NPM package, but it can also be modified to work with the babylonjs NPM package.
Component
We are going to build a reusable React component that takes care of starting up Babylon.js for us!
In a working React project you first need to install the package @babylonjs/core using npm or yarn:
npm install @babylonjs/coreyarn add @babylonjs/coreCreate a file called SceneComponent.jsx and add this:
import { useEffect, useRef } from "react";import { Engine, Scene } from "@babylonjs/core";
export default ({ antialias, engineOptions, adaptToDeviceRatio, sceneOptions, onRender, onSceneReady, ...rest }) => { const reactCanvas = useRef(null);
// set up basic engine and scene useEffect(() => { const { current: canvas } = reactCanvas;
if (!canvas) return;
const engine = new Engine(canvas, antialias, engineOptions, adaptToDeviceRatio); const scene = new Scene(engine, sceneOptions); if (scene.isReady()) { onSceneReady(scene); } else { scene.onReadyObservable.addOnce((scene) => onSceneReady(scene)); }
engine.runRenderLoop(() => { if (typeof onRender === "function") onRender(scene); scene.render(); });
const resize = () => { scene.getEngine().resize(); };
if (window) { window.addEventListener("resize", resize); }
return () => { scene.getEngine().dispose();
if (window) { window.removeEventListener("resize", resize); } }; }, [antialias, engineOptions, adaptToDeviceRatio, sceneOptions, onRender, onSceneReady]);
return <canvas ref={reactCanvas} {...rest} />;};In 45 lines we have:
- Reusable React Component for Babylon.js.
- Will resize the engine when window is resized.
- Cleans up resources automatically when unmounted.
- Any extra props you add to this component will flow to the canvas (i.e. style/className).
- We only need to add this component to a page and specify a method to run when the scene is ready. A
<canvas />element is created and a Babylon.js engine and scene are created and started.- If you want more control of the runRenderLoop, just remove it from here and add it in your
onSceneReadyprop.- TypeScript source can be copied from here.
The above component is available on npm, if you prefer:
npm add babylonjs-hookyarn add babylonjs-hookHere is a page using our component:
import React from "react";import { FreeCamera, Vector3, HemisphericLight, MeshBuilder } from "@babylonjs/core";import SceneComponent from "./SceneComponent"; // uses above component in same directory// import SceneComponent from 'babylonjs-hook'; // if you install 'babylonjs-hook' NPM.import "./App.css";
let box;
const onSceneReady = (scene) => { // This creates and positions a free camera (non-mesh) const camera = new FreeCamera("camera1", new Vector3(0, 5, -10), scene);
// This targets the camera to scene origin camera.setTarget(Vector3.Zero());
const canvas = scene.getEngine().getRenderingCanvas();
// This attaches the camera to the canvas camera.attachControl(canvas, true);
// This creates a light, aiming 0,1,0 - to the sky (non-mesh) const light = new HemisphericLight("light", new Vector3(0, 1, 0), scene);
// Default intensity is 1. Let's dim the light a small amount light.intensity = 0.7;
// Our built-in 'box' shape. box = MeshBuilder.CreateBox("box", { size: 2 }, scene);
// Move the box upward 1/2 its height box.position.y = 1;
// Our built-in 'ground' shape. MeshBuilder.CreateGround("ground", { width: 6, height: 6 }, scene);};
/** * Will run on every frame render. We are spinning the box on y-axis. */const onRender = (scene) => { if (box !== undefined) { const deltaTimeInMillis = scene.getEngine().getDeltaTime();
const rpm = 10; box.rotation.y += (rpm / 60) * Math.PI * 2 * (deltaTimeInMillis / 1000); }};
export default () => ( <div> <SceneComponent antialias onSceneReady={onSceneReady} onRender={onRender} id="my-canvas" /> </div>);A few notes on the code
This will show the basic playground on your website! A rotation has been added in the render loop (and the sphere has been changed to a box, so you can notice it).
If you would like to see this in action, there is a boilerplate project using the original babylonjs NPM package, which you can clone from:
There is a boilerplate using the above component with the ES6 NPM package @babylonjs/core here:
Adding other Babylon.js NPMs
If you start with the @babylonjs ES6 NPMs (as we have done), then add only those packages.
For GUI use
@babylonjs/guiinstead of the NPMs that don't start with@.
Conclusion
Integrating Babylon.js into a React site turns out to be not so difficult. Since Babylon.js 3.1, we can use ES6 imports not only for Babylon.js, but also for supported libraries (GUI/loaders/etc.).
If the component above does everything you need, then that's all you need and you're all set! If you want to take it a step further, you may want to take the react-babylonjs renderer for a spin.
react-babylonjs renderer
A renderer is a next-level React integration that lets you use JSX to build your scenes and GUI. State changes will flow automatically to your Babylon.js components (and persist through HMR), and you can maintain your scene graph with declarative syntax and components.
There is an NPM project called react-babylonjs that is a React renderer for Babylon.js.
What you are able to easily do is powerful, because inside the Scene component you can declare Babylon.js objects like Cameras/Meshes/Lights/Materials/Textures/3D Models/etc, using familiar JSX. Meanwhile there are escape hatches that allow you to work imperatively as well.
Babylon.js ES6 + CRA (Create React App) project examples:
react-babylonjs (https://github.com/brianzinn/react-babylonjs) links to many live storybook code examples (skybox, CSG, dynamic terrain, behaviors, GUI, react hooks, integrations (PIXI, react-spring, gsap, chroma-js), 3D models, physics, shadows, layers (glow/highlight), textures, fresnel parameters, PBR, etc.