DEV Community

Cover image for React Three Fiber: Build a 3D Scene Without Fighting React
Mark Yu
Mark Yu

Posted on • Edited on

React Three Fiber: Build a 3D Scene Without Fighting React

If your first React Three Fiber scene renders as a flat black circle, nothing is wrong with you. You probably created geometry without a useful camera, material, light, or controls.

Here is the smallest setup I would start with today.

npm create vite@latest r3f-scene -- --template react
cd r3f-scene
npm install three @react-three/fiber @react-three/drei
npm run dev
Enter fullscreen mode Exit fullscreen mode

Replace src/App.jsx with this:

import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import "./App.css";

function Sphere() {
  return (
    <mesh>
      <sphereGeometry args={[1.5, 48, 48]} />
      <meshStandardMaterial color="#e11d48" roughness={0.45} metalness={0.1} />
    </mesh>
  );
}

export default function App() {
  return (
    <main className="stage">
      <Canvas camera={{ position: [0, 1.5, 5], fov: 45 }}>
        <color attach="background" args={["#101114"]} />
        <ambientLight intensity={0.45} />
        <directionalLight position={[4, 6, 3]} intensity={1.8} />
        <Sphere />
        <OrbitControls enableDamping />
      </Canvas>
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode

And give the canvas real space:

html,
body,
#root {
  width: 100%;
  height: 100%;
  margin: 0;
}

.stage {
  width: 100vw;
  height: 100vh;
  background: #101114;
}
Enter fullscreen mode Exit fullscreen mode

That is already a complete 3D scene: canvas, camera, mesh, geometry, material, light, and controls.

The Pieces That Matter

React Three Fiber is not "Three.js but React-looking." It is a React renderer for Three.js. The JSX tags are still Three concepts.

<mesh>
  <sphereGeometry args={[1.5, 48, 48]} />
  <meshStandardMaterial color="#e11d48" />
</mesh>
Enter fullscreen mode Exit fullscreen mode

The mesh is the object. The geometry is the shape. The material is how the surface responds to light.

If you skip material, you do not have a visible surface worth looking at. If you skip light while using meshStandardMaterial, the object can look black. If you skip camera positioning, your object may be technically there but framed badly.

Do Not Start With a Model Loader

A common beginner mistake is loading a GLB model before you understand the scene. That makes debugging miserable because every issue looks like an asset problem.

Start with a primitive:

function Box() {
  return (
    <mesh rotation={[0.4, 0.4, 0]}>
      <boxGeometry args={[2, 2, 2]} />
      <meshStandardMaterial color="#22c55e" />
    </mesh>
  );
}
Enter fullscreen mode Exit fullscreen mode

If the box renders, your React, canvas, camera, lighting, and CSS are fine. Then add models.

Add Motion Carefully

Animation in React Three Fiber usually belongs in useFrame. Do not animate by putting React state updates in a render loop unless you want unnecessary rerenders.

import { useFrame } from "@react-three/fiber";
import { useRef } from "react";

function SpinningSphere() {
  const ref = useRef();

  useFrame((_, delta) => {
    ref.current.rotation.y += delta * 0.6;
  });

  return (
    <mesh ref={ref}>
      <sphereGeometry args={[1.5, 48, 48]} />
      <meshStandardMaterial color="#38bdf8" roughness={0.35} />
    </mesh>
  );
}
Enter fullscreen mode Exit fullscreen mode

This changes the Three object directly. React does not need to rerender 60 times per second just because a sphere rotates.

Add a Floor

A floating object can look fake even when the code is correct. Add a simple floor so light and position become easier to read.

function Floor() {
  return (
    <mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, -1.6, 0]}>
      <planeGeometry args={[20, 20]} />
      <meshStandardMaterial color="#27272a" />
    </mesh>
  );
}
Enter fullscreen mode Exit fullscreen mode

Then use it:

<Canvas camera={{ position: [0, 1.5, 5], fov: 45 }}>
  <color attach="background" args={["#101114"]} />
  <ambientLight intensity={0.45} />
  <directionalLight position={[4, 6, 3]} intensity={1.8} />
  <SpinningSphere />
  <Floor />
  <OrbitControls enableDamping />
</Canvas>
Enter fullscreen mode Exit fullscreen mode

My Defaults

For a first production-ish scene, I would use:

  • Vite instead of Create React App.
  • sphereGeometry, not old sphereBufferGeometry examples.
  • meshStandardMaterial for realistic light response.
  • OrbitControls while developing, even if you remove it later.
  • One ambient light and one directional light before getting fancy.
  • Fixed canvas height. Most "my scene is blank" bugs are CSS bugs.

What I Would Avoid

I would not start with physics, post-processing, GLTF models, shadows, or scroll-driven camera animation.

Those are fun, but they multiply the number of things that can be wrong. Get one primitive on screen first. Make it lit. Make it move. Then build.

Debug Checklist

  • Canvas has width and height.
  • Canvas is imported from @react-three/fiber.
  • Camera is far enough back to see the object.
  • Material matches your lighting setup.
  • Object is near [0, 0, 0].
  • DevTools console has no asset import errors.
  • Controls are enabled while debugging.

That is the base. Once this renders, everything else is iteration.

Top comments (1)

Collapse
 
louaiboumediene profile image
Louai Boumediene

One of the most things that makes me up for front-end every time i get tired of it, is 3D websites! and I love how detailed is your explanation, thanks