Build scenes visually. Add behavior with React. Ship them on the web.
A prefab is a JSON-compatible tree of nodes. Each node has components for geometry, materials, lighting, sound, or gameplay. Start with an inline prefab in your app, edit it visually, and pack assemblies to reuse within the scene. Save a separate prefab file when you want to share it across scenes or load it by URL.
npm · Website · Web editor · Starter
In a React app, install the library and its rendering dependencies:
npm install react-three-game @react-three/fiber @react-three/drei three three-textReact and React DOM are peer dependencies. Use a WebGPU-capable browser.
In Next.js, put 'use client' at the top of the canvas module.
This complete app renders a coral cube. Keep the prefab outside the component so its identity stays stable between renders:
import { GameCanvas, PrefabRoot, type Prefab } from 'react-three-game/viewer';
const prefab: Prefab = {
root: {
id: 'world',
children: [{
id: 'cube', name: 'Cube',
components: {
geometry: { type: 'Geometry', properties: { geometryType: 'box' } },
material: { type: 'Material', properties: { name: 'coral', color: '#ef806a' } },
},
}],
},
};
export default function App() {
return <div style={{ height: '100vh' }}>
<GameCanvas camera={{ position: [4, 3, 6] }}>
<ambientLight intensity={2} />
<PrefabRoot data={prefab} />
</GameCanvas>
</div>;
}Node IDs are unique within a prefab. Component keys such as geometry identify
entries on a node; type selects the registered component. Each component supplies
defaults for its properties.
Keep prefab above and replace the viewer import and App with:
import type { Prefab } from 'react-three-game/viewer';
import { PrefabEditor } from 'react-three-game/editor';
export default function App() {
return <div style={{ height: '100vh' }}>
<PrefabEditor prefab={prefab}
canvasProps={{ camera: { position: [4, 3, 6] } }}>
<ambientLight intensity={2} />
</PrefabEditor>
</div>;
}Select Cube to edit its components. The editor also exposes window.scene.
Run this in the browser console to move the cube and frame it:
const scene = window.scene;
await scene.setMode({ mode: 'edit' });
scene.update({ id: 'cube', transform: { position: [0, 1, 0] } });
scene.look({ id: 'cube' });The GUI and API share the document and undo history. Explore scene.help() and
scene.components() as you add more objects.
Select a node or assembly and choose Pack as Prefab. This keeps its components and children in place and adds packed: true. In the same console session:
scene.pack({ id: 'cube' });
scene.duplicate({ id: 'cube', newId: 'cube-2' });
scene.transform({ id: 'cube-2', position: [3, 1, 0] });
scene.duplicate({ id: 'cube', newId: 'cube-3' });
scene.transform({ id: 'cube-3', position: [-3, 1, 0] });
scene.look({ id: scene.info().rootId });The original node owns the inline definition; copies use PrefabRef with its
node ID as prefabId and have independent transforms. Edit the original's
components or children to update every copy. Unpack Prefab expands a copy
for independent editing. To retain editor changes across an
app reload, replace the inline prefab value with the JSON returned by
scene.exportJSON().
See inline composition in code and the Scene API guide for nested assemblies and export.
A custom component pairs a React view with editable properties. Save this as
Rotator.tsx and import ./Rotator in your app before mounting the editor:
import { useFrame } from '@react-three/fiber';
import { registerComponent, useNode, useGameObject,
type Component, type ComponentViewProps } from 'react-three-game/viewer';
type Props = { speed: number };
function RotatorView({ properties, enabled, children }: ComponentViewProps<Props>) {
const object = useGameObject();
const { editMode } = useNode();
useFrame((_, delta) => {
if (enabled && !editMode && object.transform) {
object.transform.rotation.y += properties.speed * delta;
}
});
return <>{children}</>;
}
const Rotator: Component<Props> = {
name: 'Rotator',
description: 'Rotate around Y. Speed is radians per second.',
properties: { speed: { default: 1, step: 0.1 } },
View: RotatorView,
};
registerComponent(Rotator);After the app reloads, select Cube, add Rotator, then press Play. Or add it through the console after reconnecting to the editor:
const scene = window.scene;
await scene.setMode({ mode: 'edit' });
scene.setComponent({ id: 'cube', key: 'rotator', component: {
type: 'Rotator', properties: { speed: 1 },
} });
await scene.setMode({ mode: 'play' });Settings are saved with the scene; the implementation stays in your app. Register custom components in both editor and playback builds. For small scripts stored with trusted scene data, see the built-in Runtime component.
Choose Save Prefab to download the current scene as JSON. scene.exportJSON()
returns the same document as a string; onSaveScene connects the editor to your
app's storage. Load a saved scene.json in the viewer from step 1 by replacing the
inline declaration with:
import prefab from './scene.json';For an assembly shared across scenes, choose Export Prefab… on that node and
save it under your app's public/prefabs/ folder. Place it with a PrefabRef URL;
see file-based reuse and loading.
Ship the JSON, its referenced assets, and custom component code with your app.
Run this repository's website and editor locally:
npm install
npm run devOpen http://localhost:3000/editor. Asset pickers and scene.assets() use
public/manifest.json, relative to the editor's basePath. Rebuild this demo
catalog with npm --prefix docs run generate-manifests.
With Node 22+, install the CLI and inspect its commands:
npm install -g react-three-game
rtg components
rtg schemaSave this as commands.json to move the cube in your exported scene.json:
{ "commands": [{ "op": "transform", "id": "cube", "position": [0, 2, 0] }] }rtg validate scene.json commands.json
rtg apply scene.json commands.json edited.json
rtg convert edited.json scene.glb ./public
rtg convert scene.glb imported.jsonPrefab JSON preserves editable components and gameplay settings. GLB conversion produces static geometry and material settings for use in other 3D tools.
| Next step | Example |
|---|---|
| Edit through scripts | Scene API |
| Compose prefabs, then load chunks | Architecture patterns |
| Connect gameplay to live objects | Host systems |
| Physics | Cool stuff demo |
| Inline buildings and movement | Parkour demo |
| Custom inspector controls | Interior mapping |
| More working scenes | Demo index |
npm run dev # Local website and editor
npm test # Library and demo tests
npm run test:package # Published package and CLI checks
npm run build # Library build
npm --prefix docs run build # Website buildLicense: see LICENSE.