Skip to content

Installation

Fresh
bash
npm install three @react-three/fiber

Compatibility

Fiber is compatible with React v18 and v19 and works with ReactDOM and React Native. @react-three/fiber@8 pairs with react@18, @react-three/fiber@9 pairs with react@19.

Getting started with React Three Fiber is not nearly as hard as you might have thought, but various frameworks may require particular attention.

Vite.js

Vite will work out of the box.

bash
# Create app
npm create vite my-app

# Select react as framework

# Install dependencies
cd my-app
npm install three @react-three/fiber

# Start development server
npm run dev

Next.js

It should work out of the box but you will encounter untranspiled add-ons in the three.js ecosystem.

Next.js 13.1 or later

Add three to transpilePackages in next.config.js:

js
transpilePackages: ['three'],

Next.js 13.0 or older

Install the next-transpile-modules module:

bash
npm install next-transpile-modules --save-dev

Then add this to your next.config.js:

js
const withTM = require('next-transpile-modules')(['three'])
module.exports = withTM()

Without Build Tools

You can use React Three Fiber with browser-ready ES Modules from esm.sh and a JSX-like syntax powered by htm.

jsx
import ReactDOM from 'https://esm.sh/react-dom'
import React, { useRef, useState } from 'https://esm.sh/react'
import { Canvas, useFrame } from 'https://esm.sh/@react-three/fiber'
import htm from 'https://esm.sh/htm'

const html = htm.bind(React.createElement)
ReactDOM.render(html`<${Canvas}>...<//>`, document.getElementById('root'))

Full example:

jsx
import ReactDOM from 'https://esm.sh/react-dom'
import React, { useRef, useState } from 'https://esm.sh/react'
import { Canvas, useFrame } from 'https://esm.sh/@react-three/fiber'
import htm from 'https://esm.sh/htm'

const html = htm.bind(React.createElement)

function Box(props) {
  const meshRef = useRef()
  const [hovered, setHover] = useState(false)
  const [active, setActive] = useState(false)
  useFrame(() => (meshRef.current.rotation.x = meshRef.current.rotation.y += 0.01))
  return html` <mesh
    ...${props}
    ref=${meshRef}
    scale=${active ? 1.5 : 1}
    onClick=${() => setActive(!active)}
    onPointerOver=${() => setHover(true)}
    onPointerOut=${() => setHover(false)}
  >
    <boxGeometry args=${[1, 1, 1]} />
    <meshStandardMaterial color=${hovered ? 'hotpink' : 'orange'} />
  </mesh>`
}

ReactDOM.render(
  html` <${Canvas}>
    <ambientLight />
    <pointLight position=${[10, 10, 10]} />
    <${Box} position=${[-1.2, 0, 0]} />
    <${Box} position=${[1.2, 0, 0]} />
  <//>`,
  document.getElementById('root'),
)

React Native

R3F v8 adds support for React Native and can be imported from @react-three/fiber/native. It uses expo-gl and expo-asset under the hood for WebGL2 bindings and ensuring interplay between Metro and three.js loaders.

Setup

bash
# Create a managed/bare app
npx create-expo-app
cd my-app

# or

# Create and link bare app
npx react-native init my-app
npx install-expo-modules@latest
cd my-app

Install dependencies:

bash
# Automatically install
expo install expo-gl

# Install NPM dependencies
npm install three @react-three/fiber

Configure Metro to handle three.js assets:

js
// metro.config.js
module.exports = {
  resolver: {
    sourceExts: ['js', 'jsx', 'json', 'ts', 'tsx', 'cjs', 'mjs'],
    assetExts: ['glb', 'gltf', 'png', 'jpg'],
  },
}

R3F's API is completely cross-platform, so you can use events and hooks just as you would on the web.

TIP

Import from @react-three/fiber/native or @react-three/drei/native for correct IntelliSense and platform-specific abstractions.

iOS Simulators

iOS simulators often have incomplete or unreliable OpenGL ES support. Always test on a physical iOS device (iPhone/iPad running iOS 16 or later) to ensure stable WebGL rendering.

jsx
import { Suspense } from 'react'
import { Canvas } from '@react-three/fiber/native'
import { useGLTF } from '@react-three/drei/native'
import modelPath from './path/to/model.glb'

function Model(props) {
  const gltf = useGLTF(modelPath)
  return <primitive {...props} object={gltf.scene} />
}

export default function App() {
  return (
    <Canvas>
      <ambientLight />
      <Suspense>
        <Model />
      </Suspense>
    </Canvas>
  )
}