Navigate
Search topics across all sections
Canvas Component
Before you can show anything in 3D, you need a place to put it. The <Canvas> component is that place. Think of your React webpage as a wall -- the Canvas is a picture frame you hang on it. Everything 3D lives inside that frame. Outside? Normal React. Inside? A whole 3D universe.
You write your first <Canvas> with a beautiful orange box inside. You save, refresh the browser, and... nothing. A blank white page stares back at you. No errors in the console. The box exists in code, but it is invisible.
The <Canvas> rendered, but its parent <div> has height: auto (which is 0px). Your entire 3D scene is there -- just squished into zero pixels.
Real-world
Imagine hanging a picture frame on your wall. The wall is your React webpage. The frame is the <Canvas>. The picture inside is your 3D world -- meshes, lights, cameras, everything.
But here is the thing: if you do not give the frame a size, it collapses flat. No frame, no picture. That is why the Canvas needs its parent container to have a width and height.
<Canvas>
You write this in JSX
Scene + Camera + Renderer
R3F creates these for you
Render Loop
60fps animation automatically
Pixels on Screen
Your 3D world appears!
Building Your First Canvas
Let us set up a Canvas step by step. It is simpler than you think -- three steps and you have a 3D scene running.
The Canvas fills whatever container it is inside. No container size means no visible canvas. A full-screen scene? Just make the parent fill the viewport.
<div style={{ width: '100vw', height: '100vh' }}>
<Canvas>
{/* 3D stuff goes here */}
</Canvas>
</div>That div is now the size of the entire browser window, and the Canvas stretches to fill it.
Most materials need light to be visible, just like real life. No light in a room? Everything is black.
<Canvas>
<ambientLight intensity={0.5} />
<directionalLight position={[5, 5, 5]} />
</Canvas>ambientLight is like opening the curtains -- soft light everywhere. directionalLight is like the sun shining from a specific direction.
A mesh is a visible 3D thing. It needs a shape (geometry) and a surface look (material). Together they make something you can actually see.
<Canvas>
<ambientLight intensity={0.5} />
<directionalLight position={[5, 5, 5]} />
<mesh>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="orange" />
</mesh>
</Canvas>That is it. A lit, orange box floating in 3D space. The Canvas gave you a camera at position [0, 0, 5] looking at the origin, so the box is right in view.
What you just learned
Canvas is the boundary between normal React and the 3D world
It automatically creates a Scene, Camera, and Renderer for you
Canvas fills its parent, so the parent must have a defined size
Lights are needed for most materials to be visible
R3F hooks (useFrame, useThree) only work inside Canvas
Question
If Canvas creates its own camera automatically, what happens when you want two different camera angles? Can you have two Canvases on the same page?
Yes, you can. Each Canvas is a completely independent 3D world with its own camera, scene, and renderer. Some apps use multiple canvases for split-screen views or picture-in-picture effects.
Think about it...
You render a <Canvas> inside a <div> that has no CSS height. What will you see on screen?
Try These Challenges
Put what you learned into practice. Try each challenge in the demo above using the Leva controls, then check the solution.
Try This!
Beginner
Remove the parent div height -- what happens?
Try This!
Beginner
Add dpr={[1, 2]} to the Canvas -- does it look sharper?
Try This!
Intermediate
Set frameloop='demand' and drag to rotate -- what changes?
These are the patterns that trip up developers most often. Switch between Wrong and Fixed to compare the code side by side.
// The Canvas has no height constraint
function App() {
return (
<div>
<Canvas>
<mesh />
</Canvas>
</div>
)
}function App() {
// This is OUTSIDE the Canvas tree!
useFrame(() => { /* ... */ })
return (
<Canvas>
<mesh />
</Canvas>
)
}<Canvas>
{/* No lights! */}
<mesh>
<boxGeometry />
<meshStandardMaterial color="orange" />
</mesh>
</Canvas>Best Practices
Set dpr limits
Use dpr={[1, 2]} to cap the pixel ratio. On Retina displays, without this your GPU works 4x harder for barely noticeable quality gains.
Use demand mode for static scenes
If nothing is animating, set frameloop="demand" so the GPU only renders when something changes. Your laptop fan will thank you.
Enable shadows explicitly
Shadows are off by default (they are expensive). Add the shadows prop only when needed, and mark meshes with castShadow and receiveShadow.
Keep the Canvas wrapper sized
Canvas fills its parent. Always ensure the parent has explicit dimensions. A common pattern: className="h-screen w-full".