Skip to content

Canvas

Fresh

The Canvas object is where you start to define your React Three Fiber scene.

jsx
import React from 'react'
import { Canvas } from '@react-three/fiber'

const App = () => (
  <Canvas>
    <pointLight position={[10, 10, 10]} />
    <mesh>
      <sphereGeometry />
      <meshStandardMaterial color="hotpink" />
    </mesh>
  </Canvas>
)

Properties

PropDescriptionDefault
childrenthree.js JSX elements or regular components
fallbackoptional DOM JSX elements in case GL is not supported
glProps for the default renderer. Accepts sync/async callback gl={defaults => new Renderer({ ...defaults })}{}
cameraProps for the default camera, or your own THREE.Camera{ fov: 75, near: 0.1, far: 1000, position: [0, 0, 5] }
sceneProps for the default scene, or your own THREE.Scene{}
shadowsProps for gl.shadowMap, can be set true for PCFsoft or: 'basic', 'percentage', 'soft', 'variance'false
raycasterProps for the default raycaster{}
frameloopRender mode: always, demand, neveralways
resizeResize config, see react-use-measure's options{ scroll: true, debounce: { scroll: 50, resize: 0 } }
orthographicCreates an orthographic camerafalse
dprPixel-ratio, use window.devicePixelRatio, or automatic: [min, max][1, 2]
legacyEnables THREE.ColorManagement in three r139 or laterfalse
linearSwitch off automatic sRGB color space and gamma correctionfalse
eventsConfiguration for the event manager, as a function of stateimport { events } from "@react-three/fiber"
eventSourceThe source where events are being subscribed to, HTMLElementgl.domElement.parentNode
eventPrefixThe event prefix cast into canvas pointer x/y eventsoffset
flatUse THREE.NoToneMapping instead of THREE.ACESFilmicToneMappingfalse
onCreatedCallback after the canvas has rendered (but not yet committed)(state) => {}
onPointerMissedResponse for pointer clicks that have missed any target(event) => {}

Defaults

Canvas uses createRoot which will create a translucent THREE.WebGLRenderer with the following constructor args:

  • antialias=true
  • alpha=true
  • powerPreference="high-performance"

And with the following properties:

  • outputColorSpace = THREE.SRGBColorSpace
  • toneMapping = THREE.ACESFilmicToneMapping

It will also create the following scene internals:

  • A THREE.Perspective camera
  • A THREE.Orthographic cam if orthographic is true
  • A THREE.PCFSoftShadowMap if shadows is true
  • A THREE.Scene (into which all the JSX is rendered) and a THREE.Raycaster

In recent versions of three.js, THREE.ColorManagement.enabled will be set to true to enable automatic conversion of colors according to the renderer's configured color space. R3F will handle texture color space conversion.

Errors and Fallbacks

On some systems WebGL may not be supported. Provide a fallback component:

jsx
<Canvas fallback={<div>Sorry no WebGL supported!</div>}>
  <mesh />
</Canvas>

Safeguard against WebGL context crashes with an error boundary:

jsx
import { useErrorBoundary } from 'use-error-boundary'

function App() {
  const { ErrorBoundary, didCatch, error } = useErrorBoundary()
  return didCatch ? (
    <div>{error.message}</div>
  ) : (
    <ErrorBoundary>
      <Canvas>
        <mesh />
      </Canvas>
    </ErrorBoundary>
  )
}

WebGPU

Recent three.js includes a WebGPU renderer. R3F supports it via an async gl prop:

tsx
import * as THREE from 'three/webgpu'
import * as TSL from 'three/tsl'
import { Canvas, extend, useFrame, useThree } from '@react-three/fiber'

declare module '@react-three/fiber' {
  interface ThreeElements extends ThreeToJSXElements<typeof THREE> {}
}

extend(THREE as any)

export default () => (
  <Canvas
    gl={async (props) => {
      const renderer = new THREE.WebGPURenderer(props as any)
      await renderer.init()
      return renderer
    }}>
      <mesh>
        <meshBasicNodeMaterial />
        <boxGeometry />
      </mesh>
  </Canvas>
)

Custom Canvas with createRoot

R3F can render to a root directly, allowing you to shave off react-dom (~40kb), react-use-measure (~3kb), and pointer events (~7kb).

jsx
import * as THREE from 'three'
import { extend, createRoot, events } from '@react-three/fiber'

// Register the THREE namespace as native JSX elements
extend(THREE)

// Create a react root
const root = createRoot(document.querySelector('canvas'))

async function app() {
  // Configure the root — must be called before render, must be awaited
  await root.configure({ events, camera: { position: [0, 0, 50] } })

  // createRoot is not responsive — handle resize yourself
  window.addEventListener('resize', () => {
    root.configure({ size: { width: window.innerWidth, height: window.innerHeight } })
  })

  window.dispatchEvent(new Event('resize'))

  root.render(<App />)

  // Unmount and dispose of memory
  // root.unmount()
}

app()

Tree-Shaking

New with v8, the underlying reconciler no longer pulls in the THREE namespace automatically. This enables tree-shaking via the extend API:

jsx
import { extend, createRoot } from '@react-three/fiber'
import { Mesh, BoxGeometry, MeshStandardMaterial } from 'three'

extend({ Mesh, BoxGeometry, MeshStandardMaterial })

createRoot(canvas).render(
  <>
    <mesh>
      <boxGeometry />
      <meshStandardMaterial />
    </mesh>
  </>,
)