Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.
React Three Fiber geometry
Inspect installed Three.js, Fiber, and Drei versions before copying constructor arguments or helper props. Examples target Fiber 9 / React 19 and Three.js r185.
Choose the representation
- Use native JSX geometry for ordinary shapes.
argsmatch the Three.js constructor; changing them reconstructs geometry, so animate transforms rather than constructor inputs. - Use
BufferGeometryfor custom topology and buffer-basedpointsfor large particle sets. Keep generated arrays stable and use deterministic generation when results must be reproducible. - Use Drei
Instancesfor convenient declarative instances with events. For large or frequently updated sets, read native instancing to avoid per-instance React overhead. - Instancing shares geometry/material and reduces draw calls. Merely sharing a geometry among separate meshes does not batch their draws.
- Drei
Mergedcreates instancing abstractions from meshes, not BufferGeometry objects; it does not concatenate arbitrary static geometry. For actual merging, inspectmergeGeometriesfromthree/addons/utils/BufferGeometryUtils.jsand ensure compatible attributes/indexing.
Declarative instances
Mount below Canvas with lighting. Each Instance belongs to its nearest Instances provider.
import { Instance, Instances } from '@react-three/drei'
export default function Example() {
return (
<Instances limit={3} range={3}>
<boxGeometry args={[0.7, 0.7, 0.7]} />
<meshStandardMaterial />
<Instance position={[-1.2, 0, 0]} color="coral" />
<Instance position={[0, 0, 0]} color="skyblue" />
<Instance position={[1.2, 0, 0]} color="gold" />
</Instances>
)
}Custom buffers and updates
positionandnormalattributes usually have item size 3; UVs have item size 2. Construct JSX buffer attributes withargs={[typedArray, itemSize]}and the correctattach, not justarray/countprops with no constructor arguments.- Indices refer to vertices; winding determines the front face. Duplicate vertices at hard normals or UV seams. Indexed vertices share all attributes, not just positions.
- Lit geometry needs normals. Use
computeVertexNormals()when appropriate; do not recompute every frame if a shader or analytic normals can express the deformation more cheaply. - After CPU buffer writes, set
attribute.needsUpdate = true. Choose dynamic usage before the first GPU upload when buffers will change often. - Recompute bounding boxes/spheres after geometry changes that affect them. GPU vertex displacement does not update CPU bounds or raycasting automatically.
- UV selection is explicit on modern Three.js:
texture.channelselectsuv,uv1,uv2, oruv3. Do not unconditionally copyuvintouv2for AO. - Avoid rebuilding buffers for pointer movement. If topology is fixed, change attribute contents or uniforms instead.
Helpers and tradeoffs
- Drei
Linesupports useful line widths; native WebGL line width is limited by the platform. CheckworldUnitswhen choosing screen-space versus world-space thickness. - Use Drei
Textfor flat text andText3Dfor extruded geometry. Current TextGeometry usesdepth, not the historicalheight; verify the helper's installed types and font format. - Use
Edgesfor sharp-edge outlines, not as a replacement for screen-space selection effects. BoundsandCenterchange framing/transforms; decide whether they should update after asset loading, resizing, or interaction.- Segment count should follow silhouette, deformation, and viewing distance. A “high quality” fixed count is not universally better.
- Declaratively created geometry can be owned by R3F. Shared or cached geometry needs a shared lifetime; do not dispose it from one instance while others remain mounted.
Verify
Check bounds/culling after updates, raycast hit positions, resource cleanup, and draw calls. Test at the intended object count; three instances do not establish performance at ten thousand.

