mrkeyoor.com_
Sat 08 Aug 22:00 UTC
npmWeb Frontendupdated 08 Aug 2026

@react-three/drei

@react-three/drei is the standard helper collection for @react-three/fiber, React's renderer for Three.js. It packages cameras and controls, Suspense-based model and texture loaders, HTML and text overlays, environment lighting, staging, shadows, instancing, performance tools, gizmos, materials, portals, and many small hooks as typed React components. It does not replace Three.js or Fiber; it sits on both and makes common scene work declarative.

Verdict

Drei is the sensible default companion for a React Three Fiber application, provided you import selectively and treat loaders, shadows, environments, and DOM overlays as real systems with costs. Do not add it to plain Three.js, an older React/Fiber stack, or a tiny scene that needs only one trivial helper.

API stability3/5Individual helpers expose typed React props and established conventions such as makeDefault, Suspense loaders, forwarding Three.js props, and imperative refs. The collection is explicitly growing, however, and its major versions track breaking changes in React, Fiber, Three.js, and many underlying helpers. Current v10 requires React 19 and Fiber 9, while component behavior can also move with three-stdlib, camera-controls, troika text, loader, and renderer changes. Pin the whole graphics stack and review migrations together.
Docs4/5The documentation has a large per-component catalog with prop signatures, focused examples, Storybook links, Suspense badges, source links, and unusually candid warnings about CDN environment presets, expensive contact shadows, declarative instancing CPU cost, transform-mode HTML blur, and native export gaps. Discoverability is good but consistency varies across a very large collection, some pages are brief API sketches, and the README contains several legacy redirect sections and points at multiple documentation hostnames.
Maintenance5/5npm 10.7.8 was published on 2026-08-05, the same day as the latest repository push, and releases regularly follow the React Three Fiber and Three.js ecosystem. The repository has 9,798 stars and 109 issues and pull requests combined, which is active-project volume for a broad component catalog rather than abandonment. Maintenance also spans current peer ranges, TypeScript declarations, web and native entry points, examples, docs, and a long list of upstream integrations.
Ecosystem5/5The package recorded 3,791,723 downloads in the measured week and is the shared helper layer for the pmndrs React 3D ecosystem. It connects Fiber scenes to three-stdlib controls and loaders, troika text, camera-controls, BVH acceleration, GPU detection, meshline, HLS video, gain maps, gesture input, Zustand, and more, with Storybook examples and community sandboxes. That breadth is why it is useful, but it also means a Drei upgrade can expose integration changes from many upstream packages.

Use it if

  • You already use React 19, @react-three/fiber 9, and Three.js, and want maintained declarative helpers instead of wrapping examples yourself
  • Your scenes repeatedly need GLTF loading, camera controls, environment maps, text, HTML annotations, bounds fitting, or instancing
  • You value components that clean up controls on unmount, work with demand rendering, and share conventions across a React 3D team
  • You can profile component-specific GPU, CPU, asset, and network costs instead of assuming every helper is cheap
Skip it if

Setup reality

Install @react-three/drei alongside its required peers: react ^19, @react-three/fiber ^9.0.0, and three >=0.159; react-dom ^19 is an optional peer but web helpers such as Html and Loader need the DOM. The package includes TypeScript declarations, CommonJS and ESM entry files, and marks itself side-effect free for tree shaking. Use named imports and measure the built application because installing Drei still brings 21 runtime dependencies and importing broad barrels or feature-heavy helpers can pull substantial code. Components and hooks that access Fiber state belong under Canvas, and asset loaders suspend, so provide a React Suspense boundary or a loading overlay. WebGL and DOM helpers also need a client-side boundary in server-rendered frameworks. Asset URLs must be deployed and CORS-readable; GLTF textures and decoder files are separate network concerns. useGLTF enables Draco support by default and downloads decoders from Google's CDN only when a compressed model needs them. Set a self-hosted decoder path when offline operation, content-security policy, or supply control matters. Environment preset names fetch remote HDR files and the docs say not to depend on them in production; self-host files instead. Drei uses three-stdlib rather than three/examples/jsm, so import matching loaders and controls from three-stdlib when extending its internals. For React Native, import @react-three/drei/native and accept the missing Html and Loader exports plus the platform setup required by Fiber native. Many helpers create render targets, event handlers, observers, or per-frame updates; convenience does not remove GPU budgets, disposal concerns, or the need to test mobile hardware.

Patterns

Add controls to a Fiber canvasadd-orbit-controls

import { Canvas } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';

export function Scene() {
  return (
    <Canvas camera={{ position: [0, 2, 5], fov: 50 }}>
      <mesh>
        <boxGeometry />
        <meshStandardMaterial color="orange" />
      </mesh>
      <OrbitControls makeDefault enableDamping />
    </Canvas>
  );
}

Drei controls must live under Canvas. makeDefault stores the controls in Fiber so Bounds, camera shake, gizmos, and other helpers can coordinate with them.

Center, light, and frame a modelstage-model

<Canvas shadows>
  <Stage
    adjustCamera={1.2}
    intensity={0.5}
    shadows="contact"
    environment={{ files: '/hdr/studio.hdr' }}
  >
    <Model />
  </Stage>
  <OrbitControls makeDefault />
</Canvas>

Stage combines several helpers and defaults to a remote city environment when given only a preset name. Pass self-hosted environment files for production and use makeDefault controls with adjustCamera.

Load a GLTF model with Suspenseload-gltf

import { Suspense } from 'react';
import { useGLTF } from '@react-three/drei';

function Model(props) {
  const { scene } = useGLTF('/models/chair.glb');
  return <primitive object={scene} {...props} />;
}

<Suspense fallback={null}>
  <Model />
</Suspense>

useGLTF suspends while loading and caches by URL. The returned scene is one object graph; use Drei's Clone or a cloned graph when mounting repeated independent copies.

Self-host Draco and preload a modelpreload-gltf

import { useGLTF } from '@react-three/drei';

useGLTF.setDecoderPath('/draco/');
useGLTF.preload('/models/compressed.glb');

function Model() {
  const gltf = useGLTF('/models/compressed.glb', '/draco/');
  return <primitive object={gltf.scene} />;
}

Draco decoding defaults to binaries on Google's CDN. Self-host the matching decoder files for offline use and strict content-security policies; preload starts network work before render.

Load named material texturesload-material-textures

const maps = useTexture({
  map: '/textures/albedo.jpg',
  normalMap: '/textures/normal.jpg',
  roughnessMap: '/textures/roughness.jpg',
});

return <meshStandardMaterial {...maps} roughness={1} />;

useTexture suspends and returns keys matching the input object. Configure color space, wrapping, repeat, and anisotropy explicitly when the texture's role requires them.

Use a self-hosted lighting environmentset-environment

<Suspense fallback={null}>
  <Environment
    files="/environments/studio.hdr"
    background={false}
    environmentIntensity={0.8}
  />
</Suspense>

Named presets fetch CDN assets and the docs say they may fail in production. Self-host HDR, EXR, gain-map, or cube-map files and include their download cost in loading UX.

Attach an occluded DOM label to 3Dattach-html-label

<mesh ref={targetRef} position={[0, 1, 0]}>
  <sphereGeometry args={[0.5, 32, 32]} />
  <meshStandardMaterial />
  <Html center occlude={[targetRef]} distanceFactor={8}>
    <button className="label">Details</button>
  </Html>
</mesh>

Html is web-only and needs react-dom. Occlusion adds raycasting work, transform mode can look blurry on some devices, and blending occlusion is rectangular unless you supply geometry.

Render crisp 3D text without a flashrender-sdf-text

<Text
  font="/fonts/Inter-Regular.woff"
  fontSize={0.4}
  color="white"
  anchorX="center"
  anchorY="middle"
  characters="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
>
  SCORE 1200
</Text>

Text uses Troika SDF rendering and suspends for font data. Supplying the needed characters lets glyph generation happen before display and avoids a flash of missing content.

Fit and clip the camera to scene boundsfit-camera-to-content

<Bounds fit clip observe margin={1.2} maxDuration={1}>
  <group>
    <Model />
  </group>
</Bounds>
<OrbitControls makeDefault />

Bounds calculation can be expensive, so call useBounds().refresh() only when content changes. Default controls must be registered for coordinated camera motion.

Render repeated meshes in one draw callinstance-repeated-meshes

<Instances limit={500} range={items.length}>
  <boxGeometry />
  <meshStandardMaterial />
  {items.map((item) => (
    <Instance
      key={item.id}
      position={item.position}
      color={item.color}
      onClick={() => select(item.id)}
    />
  ))}
</Instances>

limit allocates the instance buffers and must cover the maximum. Declarative Instance components reduce draw calls but the docs warn about CPU overhead for very large static populations such as foliage.

Render static contact shadows oncelimit-contact-shadows

<ContactShadows
  position={[0, -0.01, 0]}
  opacity={0.7}
  scale={10}
  blur={1.5}
  far={8}
  resolution={256}
  frames={1}
/>

ContactShadows is explicitly described as expensive. frames={1} is appropriate only when shadow-casting objects and lighting stay static.

Display shared Three.js loading progressshow-loading-progress

function LoadingOverlay() {
  const progress = useProgress((state) => state.progress);
  return <Html fullscreen center>{Math.round(progress)}% loaded</Html>;
}

<Suspense fallback={<LoadingOverlay />}>
  <AsyncScene />
</Suspense>

useProgress wraps THREE.DefaultLoadingManager, so it reports assets using that manager rather than every fetch in your app. A selector avoids re-rendering for unrelated loading-state fields.

Alternatives

PackageRegistryPick it when
@react-three/fibernpmUse Fiber alone and write a few local wrappers when only one or two Drei conveniences are needed
threenpmUse imperative Three.js when React reconciliation and component abstractions do not fit the render architecture
three-stdlibnpmUse maintained examples-derived controls and loaders directly when building your own Fiber components
@react-three/postprocessingnpmUse the narrower effects package when postprocessing is the only higher-level feature the scene needs