Navigate
Search topics across all sections
JSX to Three.js
Here is the big idea behind React Three Fiber: you write JSX tags like <mesh> and <boxGeometry>, and R3F translates them into real Three.js objects under the hood. You never have to call new THREE.Mesh() yourself. You write a recipe, and R3F cooks the dish.
You write <BoxGeometry> with a capital B, just like you would capitalize a React component. The browser throws an error because React is looking for a component called BoxGeometry, which does not exist. You stare at it for ten minutes.
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. Check the render method of `Scene`.
Real-world
Think of JSX as a recipe card. When you write <mesh>, you are writing an instruction: "make me a mesh." R3F is the chef who reads your recipe and actually cooks the dish -- it calls new THREE.Mesh() behind the scenes.
The recipe (JSX) is declarative: you say WHAT you want. The cooking (Three.js) is imperative: R3F figures out HOW to make it. You never touch the stove yourself.
<mesh>
You write JSX (the recipe)
R3F Reconciler
Reads the recipe
new THREE.Mesh()
Cooks the dish
scene.add(mesh)
Serves it to the scene
How the Translation Works
The naming rule is dead simple. Let us walk through it.
Take any Three.js class name and make the first letter lowercase. That is your JSX tag. No imports needed -- R3F looks it up automatically.
// THREE.Mesh -> <mesh>
// THREE.BoxGeometry -> <boxGeometry>
// THREE.PointLight -> <pointLight>
// THREE.Group -> <group>That is the entire rule. Lowercase first letter, use it as a tag. If it exists in Three.js, R3F can create it.
When Three.js constructors need parameters (like box dimensions), pass them as an array through the args prop. The order matches the Three.js docs exactly.
// new THREE.BoxGeometry(2, 2, 2)
<boxGeometry args={[2, 2, 2]} />
// new THREE.SphereGeometry(1, 32, 32)
<sphereGeometry args={[1, 32, 32]} />Think of args as the ingredients list. The recipe says "make a box that is 2 wide, 2 tall, 2 deep."
Any property you could set on a Three.js object, you can pass as a JSX prop. Arrays automatically become vectors.
// position, rotation, scale are Vector3
<mesh position={[1, 2, 3]} />
// Individual axes with dash notation
<mesh position-x={2} scale-y={1.5} />R3F does not care how Three.js sets these internally. You just say what you want, and it handles the plumbing.
What you just learned
Lowercase JSX tags map directly to Three.js classes
No imports needed -- R3F resolves them at runtime
Constructor parameters go in the args array
Object properties are set as JSX props
Arrays like [1, 2, 3] automatically become Vector3 values
Changing args recreates the object; changing props just updates it
Question
If changing args destroys and recreates the object, when would you use args vs. regular props?
Use args for things decided once (geometry size, segment count). Use props for things that change (color, position, opacity). If you animate a material color, use the color prop. If you need to change a box from 1x1x1 to 2x2x2, then args changes and R3F rebuilds the geometry.
Think about it...
What JSX would you write to create a Three.js PointLight with color white and intensity 2?
Try These Challenges
Put what you learned into practice. Try each challenge in the demo above using the Leva controls, then check the solution.
Try This!
Beginner
Try <MeshStandardMaterial> (capital M) -- what error do you get?
Try This!
Beginner
Change args={[1,1,1]} to args={[2,0.5,3]} on a boxGeometry.
Try This!
Beginner
Add rotation-x={0.5} to a mesh -- what happens?
These are the patterns that trip up developers most often. Switch between Wrong and Fixed to compare the code side by side.
// Capital letters = React looks for a component
<mesh>
<BoxGeometry args={[1, 1, 1]} />
<MeshStandardMaterial color="red" />
</mesh>// These are NOT valid props
<boxGeometry width={2} height={2} depth={2} />// R3F does not know where to put the texture
<meshStandardMaterial>
<texture image={myImage} />
</meshStandardMaterial>Best Practices
Use args for constructor params
Always pass geometry dimensions through the args array. They map directly to the Three.js constructor signature.
Prefer props over args for updates
Changing args recreates the object. If you only need to tweak a property (like color), pass it as a prop -- it updates in place without destruction.
Use dash notation sparingly
Dash notation (position-x) is great for individual axes, but for full vectors, arrays are more readable and easier to understand at a glance.
Check Three.js docs for args order
The args array maps 1:1 to the constructor parameter order in the Three.js documentation. When in doubt, look it up.