Useful add-ons for react-three-fiber providing 100+ components for 3D web applications
—
React hooks for animations, state management, and Three.js integration. These hooks provide seamless integration between React patterns and Three.js functionality, enabling declarative 3D programming.
Animation control hook for managing GLTF animations with play, pause, and crossfading capabilities.
/**
* Animation control hook for GLTF animations
* @param clips - Animation clips from GLTF loader
* @param root - Target object for animations
* @returns Animation API with controls and state
*/
function useAnimations<T extends AnimationClip>(
clips: T[],
root?: React.RefObject<Object3D>
): Api<T>;
interface Api<T extends AnimationClip> {
/** Reference to the animated object */
ref: React.RefObject<Object3D | undefined | null>;
/** Animation clips array */
clips: AnimationClip[];
/** Three.js animation mixer */
mixer: AnimationMixer;
/** Animation clip names */
names: T['name'][];
/** Animation actions mapped by name */
actions: { [key in T['name']]: AnimationAction | null };
}Usage Examples:
import { useAnimations, useGLTF } from '@react-three/drei';
function AnimatedModel() {
const group = useRef();
const { nodes, materials, animations } = useGLTF('/model.glb');
const { actions, names } = useAnimations(animations, group);
// Play animation on mount
useEffect(() => {
actions['Walk']?.play();
}, [actions]);
// Control animations
const switchAnimation = (name) => {
// Crossfade between animations
actions[name]?.reset().fadeIn(0.5).play();
Object.keys(actions).forEach((key) => {
if (key !== name) {
actions[key]?.fadeOut(0.5);
}
});
};
return (
<group ref={group}>
<primitive object={nodes.Scene} />
{/* Animation controls */}
<Html>
{names.map((name) => (
<button key={name} onClick={() => switchAnimation(name)}>
{name}
</button>
))}
</Html>
</group>
);
}GLTF model loading hook with automatic disposal and type safety.
/**
* GLTF model loading hook with automatic disposal
* @param path - Path to GLTF file
* @param useDraco - Use Draco compression, true
* @param useMeshOpt - Use MeshOpt compression, true
* @returns GLTF object with nodes, materials, and animations
*/
function useGLTF(path: string, useDraco?: boolean, useMeshOpt?: boolean): GLTF & ObjectMap;
interface GLTF {
animations: AnimationClip[];
scene: Group;
scenes: Group[];
cameras: Camera[];
asset: object;
}
interface ObjectMap {
nodes: { [name: string]: Object3D };
materials: { [name: string]: Material };
}Usage Examples:
import { useGLTF } from '@react-three/drei';
function Model({ path }) {
const { nodes, materials } = useGLTF(path);
return (
<group>
<mesh
geometry={nodes.Mesh.geometry}
material={materials.Material}
/>
</group>
);
}
// Preload models
useGLTF.preload('/model.glb');Texture loading hook with multiple format support and automatic disposal.
/**
* Texture loading hook with format support
* @param input - Texture path or array of paths
* @returns Texture or array of textures
*/
function useTexture(input: string | string[]): Texture | Texture[];Usage Examples:
import { useTexture } from '@react-three/drei';
function TexturedMesh() {
const texture = useTexture('/texture.jpg');
return (
<mesh>
<planeGeometry />
<meshStandardMaterial map={texture} />
</mesh>
);
}
// Multiple textures
function MultiTexturedMesh() {
const [colorMap, normalMap, roughnessMap] = useTexture([
'/color.jpg',
'/normal.jpg',
'/roughness.jpg'
]);
return (
<mesh>
<sphereGeometry />
<meshStandardMaterial
map={colorMap}
normalMap={normalMap}
roughnessMap={roughnessMap}
/>
</mesh>
);
}
// Preload textures
useTexture.preload('/texture.jpg');Font loading hook for text rendering with caching support.
/**
* Font loading hook for text rendering
* @param path - Path to font JSON file
* @returns Font data object
*/
function useFont(path: string): FontData;
interface FontData {
data: any;
glyphs: { [key: string]: Glyph };
}
interface Glyph {
x: number;
y: number;
width: number;
height: number;
xAdvance?: number;
xOffset?: number;
yOffset?: number;
}Usage Examples:
import { useFont, Text3D } from '@react-three/drei';
function StyledText() {
const font = useFont('/fonts/helvetiker_regular.json');
return (
<Text3D font={font} size={1} height={0.2}>
3D Text
<meshNormalMaterial />
</Text3D>
);
}
// Preload fonts
useFont.preload('/fonts/helvetiker_regular.json');Environment texture loading hook with preset support and HDRI loading.
/**
* Environment texture loading hook with presets
* @param props - Environment loader configuration
* @returns Environment texture
*/
function useEnvironment(props: EnvironmentLoaderProps): Texture;
interface EnvironmentLoaderProps {
/** Environment preset name */
preset?: PresetsType;
/** Custom environment files */
files?: string | string[];
/** Environment path */
path?: string;
/** Texture encoding */
encoding?: TextureEncoding;
}Usage Examples:
import { useEnvironment } from '@react-three/drei';
function ReflectiveSphere() {
const envMap = useEnvironment({ preset: 'sunset' });
return (
<mesh>
<sphereGeometry />
<meshStandardMaterial
envMap={envMap}
metalness={1}
roughness={0}
/>
</mesh>
);
}
// Custom HDRI environment
function CustomEnvironment() {
const envMap = useEnvironment({
files: '/hdri/studio.hdr',
encoding: RGBEEncoding
});
return (
<mesh>
<torusGeometry />
<meshStandardMaterial envMap={envMap} />
</mesh>
);
}Aspect ratio calculation hook for responsive design and viewport adaptation.
/**
* Aspect ratio calculation hook for responsive design
* @param width - Target width
* @param height - Target height
* @param factor - Scale factor, 1
* @returns Scaled dimensions [width, height, 1]
*/
function useAspect(width: number, height: number, factor?: number): [number, number, number];Usage Examples:
import { useAspect } from '@react-three/drei';
function ResponsivePlane() {
const scale = useAspect(1920, 1080, 0.5);
return (
<mesh scale={scale}>
<planeGeometry />
<meshBasicMaterial />
</mesh>
);
}
// Dynamic aspect ratio
function DynamicAspect() {
const { viewport } = useThree();
const scale = useAspect(viewport.width, viewport.height, 0.8);
return (
<mesh scale={scale}>
<planeGeometry />
<meshBasicMaterial />
</mesh>
);
}Camera access hook for getting the current scene camera.
/**
* Camera access hook for getting current camera
* @returns Current camera object
*/
function useCamera(): Camera;Usage Examples:
import { useCamera } from '@react-three/drei';
function CameraInfo() {
const camera = useCamera();
useFrame(() => {
console.log('Camera position:', camera.position);
console.log('Camera rotation:', camera.rotation);
});
return null;
}Intersection detection hook for visibility and hover effects.
/**
* Intersection detection hook for visibility effects
* @param onChange - Callback when visibility changes
* @returns Ref to attach to target object
*/
function useIntersect<T extends Object3D>(
onChange: (visible: boolean) => void
): React.RefObject<T>;Usage Examples:
import { useIntersect } from '@react-three/drei';
function VisibilityDetector() {
const [visible, setVisible] = useState(false);
const ref = useIntersect((isVisible) => setVisible(isVisible));
return (
<mesh ref={ref}>
<boxGeometry />
<meshStandardMaterial
color={visible ? 'green' : 'red'}
emissive={visible ? 'darkgreen' : 'darkred'}
/>
</mesh>
);
}Depth buffer hook for accessing scene depth information.
/**
* Depth buffer hook for accessing depth information
* @param options - Depth buffer configuration
* @returns Depth buffer texture and controls
*/
function useDepthBuffer(options?: {
size?: number;
frames?: number;
}): {
buffer: WebGLRenderTarget;
camera: Camera;
}Context bridging hook for sharing React context across R3F boundaries.
/**
* Context bridging hook for sharing React context
* @param contexts - Array of React contexts to bridge
* @returns Bridge component
*/
function useContextBridge(...contexts: React.Context<any>[]): React.ComponentType<{ children: React.ReactNode }>;Usage Examples:
import { useContextBridge } from '@react-three/drei';
const ThemeContext = React.createContext();
const UserContext = React.createContext();
function Scene() {
const ContextBridge = useContextBridge(ThemeContext, UserContext);
return (
<ContextBridge>
<mesh>
<ThemedMaterial />
</mesh>
</ContextBridge>
);
}Box-projected environment mapping hook for realistic interior reflections.
/**
* Box-projected environment mapping hook
* @param envMap - Environment map texture
* @param position - Box position
* @param size - Box size
* @returns Box-projected environment uniforms
*/
function useBoxProjectedEnv(
envMap: Texture,
position: [number, number, number],
size: [number, number, number]
): {
envMap: Texture;
cubeMapSize: Vector3;
cubeMapPos: Vector3;
};function AnimationSequence() {
const group = useRef();
const { actions } = useAnimations(animations, group);
const [currentAnimation, setCurrentAnimation] = useState('idle');
const playSequence = async () => {
await playAnimation('walk', 2000);
await playAnimation('run', 3000);
await playAnimation('idle', 1000);
};
const playAnimation = (name, duration) => {
return new Promise((resolve) => {
actions[name]?.reset().play();
setTimeout(resolve, duration);
});
};
return (
<group ref={group}>
<primitive object={scene} />
</group>
);
}function ResponsiveTexture() {
const { viewport } = useThree();
const isMobile = viewport.width < 768;
const texture = useTexture(
isMobile ? '/textures/low-res.jpg' : '/textures/high-res.jpg'
);
return (
<mesh>
<planeGeometry />
<meshStandardMaterial map={texture} />
</mesh>
);
}function DynamicEnvironment() {
const [preset, setPreset] = useState('sunset');
const envMap = useEnvironment({ preset });
useEffect(() => {
const handleTimeChange = () => {
const hour = new Date().getHours();
if (hour < 6) setPreset('night');
else if (hour < 12) setPreset('dawn');
else if (hour < 18) setPreset('city');
else setPreset('sunset');
};
handleTimeChange();
const interval = setInterval(handleTimeChange, 60000);
return () => clearInterval(interval);
}, []);
return (
<mesh>
<sphereGeometry />
<meshStandardMaterial envMap={envMap} />
</mesh>
);
}// Preload assets for better performance
const preloadAssets = () => {
useGLTF.preload('/models/character.glb');
useTexture.preload(['/textures/diffuse.jpg', '/textures/normal.jpg']);
useFont.preload('/fonts/roboto.json');
};
// Call during app initialization
useEffect(preloadAssets, []);
// Automatic cleanup
function Model({ url }) {
const gltf = useGLTF(url);
// Automatic disposal on unmount
useEffect(() => {
return () => gltf.dispose?.();
}, [gltf]);
return <primitive object={gltf.scene} />;
}Hook for creating and managing depth buffer textures.
/**
* Creates and manages depth buffer textures for depth-based effects
* @param config - Depth buffer configuration
* @returns Depth texture
*/
function useDepthBuffer(config?: DepthBufferConfig): DepthTexture;
interface DepthBufferConfig {
/** Buffer size, 256 */
size?: number;
/** Update frames, Infinity */
frames?: number;
}Hook for bridging React contexts across portals and different render trees.
/**
* Bridges React contexts across portals and render trees
* @param contexts - React contexts to bridge
* @returns Context bridge component
*/
function useContextBridge(...contexts: Array<React.Context<any>>): React.ComponentType<{children: React.ReactNode}>;Hook for box-projected environment mapping on materials.
/**
* Applies box-projected environment mapping to materials
* @param envMap - Environment map texture
* @param config - Box projection configuration
* @returns Box projection utilities
*/
function useBoxProjectedEnv(
envMap: Texture,
config: BoxProjectedEnvConfig
): BoxProjectedEnvResult;
interface BoxProjectedEnvConfig {
/** Environment map size */
envMapSize: [number, number, number];
/** Environment map position */
envMapPosition: [number, number, number];
}
interface BoxProjectedEnvResult {
/** Apply to material */
apply: (material: Material) => void;
}Install with Tessl CLI
npx tessl i tessl/npm-react-three--drei