
r3f-geometry
PopularBuild React Three Fiber geometry, custom buffers, instanced meshes, points, and lines. Use for shape construction and geometry draw-call optimization, rather than material appearance.
Build React Three Fiber geometry, custom buffers, instanced meshes, points, and lines. Use for shape construction and geometry draw-call optimization, rather than material appearance.
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.





