Useful add-ons for react-three-fiber providing 100+ components for 3D web applications
—
Interactive tools and visual helpers for development and user interaction. These components provide intuitive interfaces for object manipulation, scene navigation, and visual debugging.
Interactive pivot controls for object transformation with visual feedback and constraint options.
/**
* Interactive pivot controls for object transformation
* @param props - Pivot controls configuration
* @returns JSX element for pivot controls
*/
function PivotControls(props: PivotControlsProps): JSX.Element;
interface PivotControlsProps {
/** Enable/disable controls, true */
enabled?: boolean;
/** Gizmo scale, 1 */
scale?: number;
/** Line width for control handles, 2.5 */
lineWidth?: number;
/** Fixed screen size (pixels), false */
fixed?: boolean;
/** Position offset from object */
offset?: [number, number, number];
/** Starting rotation */
rotation?: [number, number, number];
/** Transform matrix */
matrix?: Matrix4;
/** Anchor point for transformations */
anchor?: [number, number, number];
/** Display mode flags */
displayValues?: boolean;
/** Disable X axis, false */
disableAxisX?: boolean;
/** Disable Y axis, false */
disableAxisY?: boolean;
/** Disable Z axis, false */
disableAxisZ?: boolean;
/** Disable rotation, false */
disableRotations?: boolean;
/** Disable scaling, false */
disableScaling?: boolean;
/** Disable sliders, false */
disableSliders?: boolean;
/** Active axes color */
activeAxes?: [boolean, boolean, boolean];
/** Translation limits */
translationLimits?: [[number, number], [number, number], [number, number]];
/** Rotation limits */
rotationLimits?: [[number, number], [number, number], [number, number]];
/** Scale limits */
scaleLimits?: [[number, number], [number, number], [number, number]];
/** Drag start callback */
onDragStart?: () => void;
/** Drag callback */
onDrag?: (local: Matrix4, deltaL: Matrix4, world: Matrix4, deltaW: Matrix4) => void;
/** Drag end callback */
onDragEnd?: () => void;
}Usage Examples:
import { PivotControls } from '@react-three/drei';
// Basic pivot controls
<PivotControls>
<mesh>
<boxGeometry />
<meshStandardMaterial />
</mesh>
</PivotControls>
// Advanced pivot controls with constraints
<PivotControls
scale={100}
lineWidth={3}
fixed
anchor={[0, 0, 0]}
disableRotations
translationLimits={[[-5, 5], [0, 10], [-5, 5]]}
onDragStart={() => console.log('Drag started')}
onDrag={(local, deltaL, world, deltaW) => {
// Handle transformation
console.log('Local matrix:', local);
console.log('World matrix:', world);
}}
onDragEnd={() => console.log('Drag ended')}
>
<mesh>
<sphereGeometry />
<meshStandardMaterial color="orange" />
</mesh>
</PivotControls>
// Multiple constrained pivot controls
function ConstrainedObject() {
const [position, setPosition] = useState([0, 0, 0]);
return (
<PivotControls
anchor={[0, 0, 0]}
disableAxisY
onDrag={(local) => {
const pos = new Vector3().setFromMatrixPosition(local);
setPosition([pos.x, 0, pos.z]); // Constrain to ground plane
}}
>
<mesh position={position}>
<cylinderGeometry />
<meshStandardMaterial />
</mesh>
</PivotControls>
);
}Drag controls for intuitive object manipulation with 3D mouse interaction.
/**
* Drag controls for object manipulation
* @param props - Drag controls configuration
* @returns JSX element for drag controls
*/
function DragControls(props: DragControlsProps): JSX.Element;
interface DragControlsProps {
/** Child objects to make draggable */
children: React.ReactNode;
/** Axis constraints: 'x' | 'y' | 'z' | 'xy' | 'xz' | 'yz' */
axisLock?: 'x' | 'y' | 'z' | 'xy' | 'xz' | 'yz';
/** Auto transform objects, true */
autoTransform?: boolean;
/** Transform matrix */
matrix?: Matrix4;
/** Drag start callback */
onDragStart?: (event: { object: Object3D; pointerId: number; point: Vector3 }) => void;
/** Drag callback */
onDrag?: (event: { object: Object3D; pointerId: number; point: Vector3; delta: Vector3 }) => void;
/** Drag end callback */
onDragEnd?: (event: { object: Object3D; pointerId: number }) => void;
/** Hover start callback */
onHoverStart?: (event: { object: Object3D }) => void;
/** Hover end callback */
onHoverEnd?: (event: { object: Object3D }) => void;
}Usage Examples:
import { DragControls } from '@react-three/drei';
// Basic drag controls
<DragControls>
<mesh>
<boxGeometry />
<meshStandardMaterial />
</mesh>
<mesh position={[2, 0, 0]}>
<sphereGeometry />
<meshStandardMaterial />
</mesh>
</DragControls>
// Constrained dragging
<DragControls
axisLock="y"
onDragStart={(e) => console.log('Dragging:', e.object.name)}
onDrag={(e) => {
// Custom drag behavior
e.object.rotation.y += e.delta.x * 0.01;
}}
>
<mesh name="draggable-box">
<boxGeometry />
<meshStandardMaterial color="red" />
</mesh>
</DragControls>
// Advanced drag system
function DraggableSystem() {
const [draggedObject, setDraggedObject] = useState(null);
return (
<DragControls
onDragStart={(e) => {
setDraggedObject(e.object);
e.object.material.emissive.setHex(0x444444);
}}
onDragEnd={(e) => {
setDraggedObject(null);
e.object.material.emissive.setHex(0x000000);
}}
onHoverStart={(e) => {
document.body.style.cursor = 'grab';
}}
onHoverEnd={(e) => {
document.body.style.cursor = 'auto';
}}
>
{objects.map((obj, i) => (
<DraggableObject key={i} {...obj} />
))}
</DragControls>
);
}3D grid helper for spatial reference and scene organization.
/**
* 3D grid helper for spatial reference
* @param props - Grid configuration
* @returns JSX element for grid display
*/
function Grid(props: GridProps): JSX.Element;
interface GridProps extends Omit<ThreeElements['gridHelper'], 'ref'> {
/** Grid cell size, 1 */
cellSize?: number;
/** Grid section size, 10 */
sectionSize?: number;
/** Grid color, #999999 */
cellColor?: ReactThreeFiber.Color;
/** Section color, #555555 */
sectionColor?: ReactThreeFiber.Color;
/** Grid size, 100 */
args?: [number, number];
/** Infinite grid, false */
infiniteGrid?: boolean;
/** Fade distance, 100 */
fadeDistance?: number;
/** Fade strength, 1 */
fadeStrength?: number;
/** Follow camera, false */
followCamera?: boolean;
/** Side to render, DoubleSide */
side?: Side;
}
type GridMaterialType = {
cellSize: number;
sectionSize: number;
cellColor: Color;
sectionColor: Color;
fadeDistance: number;
fadeStrength: number;
};Usage Examples:
import { Grid } from '@react-three/drei';
// Basic grid
<Grid args={[100, 100]} />
// Customized grid
<Grid
cellSize={0.5}
sectionSize={5}
cellColor={'#ff0000'}
sectionColor={'#0000ff'}
args={[50, 50]}
position={[0, -1, 0]}
/>
// Infinite grid that follows camera
<Grid
infiniteGrid
fadeDistance={50}
fadeStrength={2}
followCamera
cellSize={1}
sectionSize={10}
/>Container for 3D gizmos with positioning and interaction management.
/**
* Container for 3D gizmos with positioning
* @param props - Gizmo helper configuration
* @returns JSX element for gizmo container
*/
function GizmoHelper(props: GizmoHelperProps): JSX.Element;
interface GizmoHelperProps {
/** Gizmo alignment, 'bottom-right' */
alignment?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
/** Gizmo margin, [80, 80] */
margin?: [number, number];
/** Render on top, false */
onTop?: boolean;
/** Auto update, false */
onUpdate?: () => void;
/** Render priority, 1 */
renderPriority?: number;
/** Child gizmos */
children: React.ReactNode;
}Usage Examples:
import { GizmoHelper, GizmoViewport } from '@react-three/drei';
// Basic gizmo helper
<GizmoHelper alignment="bottom-right" margin={[80, 80]}>
<GizmoViewport
axisColors={['red', 'green', 'blue']}
labelColor="black"
/>
</GizmoHelper>3D viewport gizmo for camera orientation display and interaction.
/**
* 3D viewport gizmo for camera orientation
* @param props - Gizmo viewport configuration
* @returns JSX element for viewport gizmo
*/
function GizmoViewport(props: GizmoViewportProps): JSX.Element;
interface GizmoViewportProps {
/** Axis colors [x, y, z], ['red', 'green', 'blue'] */
axisColors?: [string, string, string];
/** Axis head scale, 1 */
axisHeadScale?: number;
/** Label color, 'black' */
labelColor?: string;
/** Axis scale, [0.8, 0.05, 0.8] */
axisScale?: [number, number, number];
/** Labels ['X', 'Y', 'Z'] */
labels?: [string, string, string];
/** Font size, 16 */
fontSize?: number;
/** Font family, 'Inter' */
font?: string;
}3D viewcube gizmo for camera navigation and preset views.
/**
* 3D viewcube gizmo for camera navigation
* @param props - Gizmo viewcube configuration
* @returns JSX element for viewcube gizmo
*/
function GizmoViewcube(props: GizmoViewcubeProps): JSX.Element;
interface GizmoViewcubeProps {
/** Face text color, 'white' */
textColor?: string;
/** Face background color, '#f0f0f0' */
color?: string;
/** Hover color, '#999' */
hoverColor?: string;
/** Edge color, 'black' */
strokeColor?: string;
/** Font size, 0.5 */
fontSize?: number;
/** Font family, 'Inter' */
font?: string;
/** Face labels */
faces?: string[];
/** Corner radius, 0.1 */
cornerRadius?: number;
/** Opacity, 1 */
opacity?: number;
}Usage Examples:
import { GizmoHelper, GizmoViewcube, GizmoViewport } from '@react-three/drei';
// Complete gizmo system
<GizmoHelper alignment="bottom-right" margin={[100, 100]}>
<GizmoViewcube
faces={['Right', 'Left', 'Top', 'Bottom', 'Front', 'Back']}
color="#f8f8f8"
hoverColor="#ddd"
strokeColor="#666"
textColor="#333"
fontSize={0.6}
/>
</GizmoHelper>
// Combined viewport and viewcube
<GizmoHelper alignment="top-left" margin={[80, 80]}>
<group>
<GizmoViewcube />
<GizmoViewport
axisColors={['#ff3653', '#8adb00', '#2c8fff']}
labelColor="white"
/>
</group>
</GizmoHelper>Advanced transformation gizmo with multiple modes and precise control.
/**
* Advanced transformation gizmo (from core controls)
* @param props - Transform controls configuration
* @returns JSX element for transformation gizmo
*/
function TransformControls(props: TransformControlsProps): JSX.Element;
interface TransformControlsProps extends Omit<ThreeElement<TransformControlsImpl>, 'ref' | 'args'> {
/** Object to transform */
object?: Object3D;
/** Camera to use */
camera?: Camera;
/** DOM element for events */
domElement?: HTMLElement;
/** Control mode: 'translate' | 'rotate' | 'scale' */
mode?: 'translate' | 'rotate' | 'scale';
/** Transform space: 'world' | 'local' */
space?: 'world' | 'local';
/** Gizmo size, 1 */
size?: number;
/** Show X axis, true */
showX?: boolean;
/** Show Y axis, true */
showY?: boolean;
/** Show Z axis, true */
showZ?: boolean;
/** Translation snap */
translationSnap?: number;
/** Rotation snap */
rotationSnap?: number;
/** Scale snap */
scaleSnap?: number;
/** Dragging change callback */
onDragging?: (dragging: boolean) => void;
/** Object change callback */
onChange?: () => void;
}function EditorScene() {
const [selectedObject, setSelectedObject] = useState(null);
const [transformMode, setTransformMode] = useState('translate');
const [showGrid, setShowGrid] = useState(true);
return (
<>
{/* Scene grid */}
{showGrid && (
<Grid
cellSize={1}
sectionSize={10}
args={[100, 100]}
position={[0, -0.01, 0]}
/>
)}
{/* Draggable objects */}
<DragControls
onDragStart={(e) => setSelectedObject(e.object)}
onDragEnd={() => setSelectedObject(null)}
>
<SceneObjects />
</DragControls>
{/* Transform controls for selected object */}
{selectedObject && (
<TransformControls
object={selectedObject}
mode={transformMode}
onDragging={(dragging) => {
// Disable orbit controls while transforming
orbitControlsRef.current.enabled = !dragging;
}}
/>
)}
{/* Camera gizmos */}
<GizmoHelper alignment="bottom-right" margin={[80, 80]}>
<GizmoViewcube />
<GizmoViewport />
</GizmoHelper>
{/* UI Controls */}
<Html fullscreen>
<div className="editor-ui">
<button onClick={() => setTransformMode('translate')}>
Translate
</button>
<button onClick={() => setTransformMode('rotate')}>
Rotate
</button>
<button onClick={() => setTransformMode('scale')}>
Scale
</button>
<button onClick={() => setShowGrid(!showGrid)}>
Toggle Grid
</button>
</div>
</Html>
</>
);
}function ConstrainedEditor() {
return (
<>
{/* Ground-constrained dragging */}
<DragControls axisLock="xz">
<GroundObjects />
</DragControls>
{/* Y-axis only movement */}
<PivotControls
disableAxisX
disableAxisZ
disableRotations
disableScaling
translationLimits={[null, [0, 10], null]}
>
<ElevatorPlatform />
</PivotControls>
{/* Rotation-only controls */}
<PivotControls
anchor={[0, 0, 0]}
disableSliders
disableScaling
rotationLimits={[null, [-Math.PI, Math.PI], null]}
>
<RotatingDoor />
</PivotControls>
</>
);
}function GizmoSystem() {
const [gizmoVisible, setGizmoVisible] = useState(true);
const [gizmoMode, setGizmoMode] = useState('translate');
const [activeObject, setActiveObject] = useState(null);
// Keyboard shortcuts
useEffect(() => {
const handleKeyDown = (e) => {
switch (e.key) {
case 'g':
setGizmoMode('translate');
break;
case 'r':
setGizmoMode('rotate');
break;
case 's':
setGizmoMode('scale');
break;
case 'h':
setGizmoVisible(!gizmoVisible);
break;
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [gizmoVisible]);
return (
<>
{gizmoVisible && (
<>
<Grid infiniteGrid fadeDistance={100} />
<PivotControls
enabled={activeObject !== null}
scale={gizmoMode === 'scale' ? 150 : 100}
onDragStart={() => console.log(`${gizmoMode} started`)}
onDragEnd={() => console.log(`${gizmoMode} ended`)}
>
{activeObject && <primitive object={activeObject} />}
</PivotControls>
<GizmoHelper alignment="bottom-right">
<GizmoViewcube />
</GizmoHelper>
</>
)}
</>
);
}function OptimizedGizmos() {
// Only render gizmos when needed
const [showGizmos, setShowGizmos] = useState(false);
const [selectedObjects, setSelectedObjects] = useState([]);
// Throttle gizmo updates
const throttledUpdate = useCallback(
throttle((matrix) => {
// Update object transform
updateObjectTransform(matrix);
}, 16), // 60 FPS
[]
);
return (
<>
{/* Conditional gizmo rendering */}
{showGizmos && selectedObjects.length > 0 && (
<PivotControls
onDrag={throttledUpdate}
lineWidth={window.devicePixelRatio > 1 ? 1 : 2}
>
{selectedObjects.map(obj => (
<primitive key={obj.uuid} object={obj} />
))}
</PivotControls>
)}
{/* Low-impact grid */}
<Grid
infiniteGrid
fadeDistance={50}
cellSize={1}
args={[20, 20]} // Smaller grid for performance
/>
</>
);
}Shape utility for creating rounded box geometries.
/**
* Creates rounded box geometry with configurable corner radius
* @param props - RoundedBox configuration props
* @returns JSX element for rounded box mesh
*/
function RoundedBox(props: RoundedBoxProps): JSX.Element;
interface RoundedBoxProps extends Omit<ThreeElements['mesh'], 'ref' | 'args'> {
/** Box dimensions [width, height, depth] */
args?: [number?, number?, number?];
/** Corner radius, 0.05 */
radius?: number;
/** Surface smoothness, 4 */
smoothness?: number;
/** Bevel segments for corners, 3 */
bevelSegments?: number;
/** Extrude steps, 1 */
steps?: number;
/** Crease angle for sharp edges, 0.4 */
creaseAngle?: number;
}Utility component for creating screen-aligned quad geometries.
/**
* Creates screen-aligned quad geometry for post-processing effects
* @param props - ScreenQuad configuration props
* @returns JSX element for screen quad mesh
*/
function ScreenQuad(props: ScreenQuadProps): JSX.Element;
interface ScreenQuadProps extends Omit<ThreeElements['mesh'], 'ref'> {
/** Material for the quad */
material?: Material;
}Install with Tessl CLI
npx tessl i tessl/npm-react-three--drei