Appearance
v9 Migration Guide
FreshThis is a compatibility release for React 19, which brings further performance, stability, and type improvements.
Breaking Changes
This release contains breaking changes when using Strict Mode, which can highlight bugs during development. See the StrictMode section below.
Features
useLoader Accepts Loader Instance
useLoader now supports re-use of external loader instances for more controlled pooling and setup:
jsx
import { GLTFLoader } from 'three/addons'
import { useLoader } from '@react-three/fiber'
function Model() {
const gltf = useLoader(GLTFLoader, '/path/to/model.glb')
// ...
}
// or with a pre-configured loader instance
const loader = new GLTFLoader()
function Model() {
const gltf = useLoader(loader, '/path/to/model.glb')
// ...
}Factory extend Signature
extend can now produce a component when a three.js class is passed individually instead of a catalog of named classes. This is backwards compatible and reduces TypeScript boilerplate:
tsx
import { OrbitControls } from 'three/addons'
import { type ThreeElement, type ThreeElements } from '@react-three/fiber'
declare module '@react-three/fiber' {
interface ThreeElements {
orbitControls: ThreeElement<typeof OrbitControls>
}
}
extend({ OrbitControls })
<orbitControls args={[camera, gl.domElement]}>
// or,
const Controls = extend(OrbitControls)
<Controls args={[camera, gl.domElement]} />Async GL Prop
The Canvas GL prop callback now passes constructor parameters instead of just a canvas reference:
diff
<Canvas
gl={{ reverseDepthBuffer: true }}
- gl={(canvas) => new WebGLRenderer({ canvas })}
+ gl={(props) => new WebGLRenderer(props)}
>A callback passed to GL can now return a promise for async constructors like WebGPURenderer:
tsx
<Canvas
gl={async (props) => {
// ...
return renderer
}}
>WebGPU Support
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>
)Fixes
Color Management of Textures
Automatic sRGB conversion of texture props has been removed. Color textures are now handled automatically for built-in materials, aligning with vanilla three.js behavior. For custom materials or shaders, annotate color textures:
jsx
texture.colorSpace = THREE.SRGBColorSpace
// or in JSX:
texture-colorSpace={THREE.SRGBColorSpace}Suspense and Side-Effects
Side-effects like attach and constructor effects no longer fire repeatedly without proper cleanup during suspension:
jsx
import { ThreeElement, useThree } from '@react-three/fiber'
import { OrbitControls } from 'three/addons'
declare module '@react-three/fiber' {
interface ThreeElements {
OrbitControls: ThreeElement<typeof OrbitControls>
}
}
extend({ OrbitControls })
function Controls() {
const camera = useThree((state) => state.camera)
const gl = useThree((state) => state.gl)
// Will only initialize when tree is connected to screen
return <orbitControls args={[camera, gl.domElement]}>
}
<Suspense>
<Controls />
<AsyncComponent />
</Suspense>Swapping with Args and Primitives
Swapping elements when changing the args or primitive object prop has been improved for structured children like arrays or iterators.
TypeScript Changes
Props Renamed to CanvasProps
diff
-function Canvas(props: Props)
+function Canvas(props: CanvasProps)Dynamic JSX Types
Hardcoded exports like MeshProps have been removed:
diff
-import { MeshProps } from '@react-three/fiber'
-type Props = MeshProps
+import { ThreeElements } from '@react-three/fiber'
+type Props = ThreeElements['mesh']Node Helpers Removed
Specialized Node type helpers are removed and combined into ThreeElement:
tsx
import { type ThreeElement } from '@react-three/fiber'
declare module '@react-three/fiber' {
interface ThreeElements {
customElement: ThreeElement<typeof CustomElement>
}
}
extend({ CustomElement })ThreeElements (Current Pattern)
diff
-import { type Node } from '@react-three/fiber'
-
-declare global {
- namespace JSX {
- interface IntrinsicElements {
- customElement: Node<CustomElement, typeof CustomElement>
- }
- }
-}
-
-extend({ CustomElement })
+import { type ThreeElement } from '@react-three/fiber'
+
+declare module '@react-three/fiber' {
+ interface ThreeElements {
+ customElement: ThreeElement<typeof CustomElement>
+ }
+}
+
+extend({ CustomElement })Testing
StrictMode
StrictMode is now correctly inherited from a parent renderer:
diff
<StrictMode>
<Canvas>
- <StrictMode>
- // ...
- </StrictMode>
+ // ...
</Canvas>
</StrictMode>WARNING
This change may affect the behavior of your application. If you encounter anything that worked before and fails now, profile it first in dev and then production. If it works in prod then Strict Mode has flushed out a side-effect in your code.
Act
act is now exported from React itself and can be used for all renderers:
tsx
import { act } from 'react'
import { createRoot } from '@react-three/fiber'
const store = await act(async () => createRoot(canvas).render(<App />))
console.log(store.getState())