🇨🇭 A React renderer for Three.js (web and react-native)

Last update: May 15, 2022


Version Downloads Twitter Discord Open Collective ETH BTC

react-three-fiber is a React renderer for threejs on the web and react-native.

npm install three react-three-fiber

These demos are real, you can click them! They contain the full code, too.


Building dynamic scene graphs declaratively with re-usable components makes dealing with threejs easier and brings order and sanity to your codebase. These components react to state changes, are interactive out of the box and can tap into React's infinite ecosystem.

Does it have limitations?

None. Everything that works in threejs will work here. In contrast to "bindings" where a library ships/maintains dozens of wrapper components, it just renders JSX to threejs dynamically: <mesh /> simply is another expression for new THREE.Mesh(). It does not know or target a specific threejs version nor does it need updates for modified, added or removed upstream features.

Is it slower than raw threejs?

No. Rendering performance is up to threejs and the GPU. Components participate in the renderloop outside of React, without any additional overhead. React is otherwise very efficient in building and managing component-trees, it could potentially outperform manual/imperative apps at scale.

What does it look like?

Let's make a re-usable component that has its own state, reacts to user-input and participates in the render-loop. (live demo).
import ReactDOM from 'react-dom'
import React, { useRef, useState } from 'react'
import { Canvas, useFrame } from 'react-three-fiber'

function Box(props) {
  // This reference will give us direct access to the mesh
  const mesh = useRef()

  // Set up state for the hovered and active state
  const [hovered, setHover] = useState(false)
  const [active, setActive] = useState(false)

  // Rotate mesh every frame, this is outside of React without overhead
  useFrame(() => {
    mesh.current.rotation.x = mesh.current.rotation.y += 0.01

  return (
      scale={active ? [1.5, 1.5, 1.5] : [1, 1, 1]}
      onClick={(event) => setActive(!active)}
      onPointerOver={(event) => setHover(true)}
      onPointerOut={(event) => setHover(false)}>
      <boxBufferGeometry args={[1, 1, 1]} />
      <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />

    <ambientLight />
    <pointLight position={[10, 10, 10]} />
    <Box position={[-1.2, 0, 0]} />
    <Box position={[1.2, 0, 0]} />
Show TypeScript example
import ReactDOM from 'react-dom'
import React, { useRef, useState } from 'react'
import { Canvas, MeshProps, useFrame } from 'react-three-fiber'
import type { Mesh } from 'three'

const Box: React.FC<MeshProps> = (props) => {
  // This reference will give us direct access to the mesh
  const mesh = useRef<Mesh>()

  // Set up state for the hovered and active state
  const [hovered, setHover] = useState(false)
  const [active, setActive] = useState(false)

  // Rotate mesh every frame, this is outside of React without overhead
  useFrame(() => {
    if (mesh.current) mesh.current.rotation.x = mesh.current.rotation.y += 0.01

  return (
      scale={active ? [1.5, 1.5, 1.5] : [1, 1, 1]}
      onClick={(event) => setActive(!active)}
      onPointerOver={(event) => setHover(true)}
      onPointerOut={(event) => setHover(false)}>
      <boxBufferGeometry args={[1, 1, 1]} />
      <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />

    <ambientLight />
    <pointLight position={[10, 10, 10]} />
    <Box position={[-1.2, 0, 0]} />
    <Box position={[1.2, 0, 0]} />



You need to be versed in both React and Threejs before rushing into this. If you are unsure about React consult the official React docs, especially the section about hooks. As for Threejs, make sure you at least glance over the following links:

  1. Make sure you have a basic grasp of Threejs. Keep that site open.
  2. When you know what a scene is, a camera, mesh, geometry, material, fork the demo above.
  3. Look up the JSX elements that you see (mesh, ambientLight, etc), all threejs exports are native to three-fiber.
  4. Try changing some values, scroll though our Api to see what the various settings and hooks do.

Some reading material:


How to contribute

If you like this project, please consider helping out. All contributions are welcome as well as donations to Opencollective, or in crypto BTC: 36fuguTPxGCNnYZSRdgdh6Ea94brCAjMbH, ETH: 0x6E3f79Ea1d0dcedeb33D3fC6c34d2B1f156F2682.


  • 1. TypeScript

    Any interest in supporting TypeScript? Initially probably only the inner workings (react-reconciler provides types) and then finding a way to generate types for the THREE bindings.

    Reviewed by ksjogo at 2019-03-07 18:20
  • 2. help wanted for creating a website

    👋anyone wants to help make a good webpage for three-fiber? i am trying to get something going but i have no idea what i'm doing, layout and typography is out of my league completely.

    this is what i've got so far from playing around:


    live demo here: https://pjcc1.csb.app/ (editor: https://codesandbox.io/s/three-fiber-useloader-pjcc1)

    as always, i would be happy to credit you plus link on the site.

    as for the requirements ...

    it doesn't have to look like the above at all.

    it could be really simple or really fancy.

    it could have docs or none at all.

    it could even be a small interactive tutorial, like this one: https://use-key-state.mihaicernusca.com

    basically, if you have ideas, make a proposal! 😊

    Reviewed by drcmda at 2019-10-06 22:00
  • 3. onPointerOut doesn't fire when moving to an abutting object

    I made a sandbox to demonstrate the differences between R3F pointer events and native DOM. (The ts-ignore in the sandbox are there because onPointerEnter and onPointerLeave seem to be missing from the EventHandlers type in types.ts: I can make a PR to add that if that's helpful.)

    To see the DOM event behaviour, move the mouse onto the A at the top, then from A directly to B (there's no gap between them). The move from A to B gives these events:

    DOM out A 
    DOM child leave A 
    DOM child enter B 
    DOM over B 

    You can see the equivalent situation in R3F below. Move your pointer onto the red cube, then directly onto the blue cube. Again, there's no gap between them. When you move from red to blue, you get these events:

    R3F child enter bravo 
    R3F child leave alpha

    Note that the leave and enter are the opposite way around compared to the native events; more on this later. More importantly, note the absence of the "out" event. This comes later, when you move out from the blue cube onto empty space:

    R3F out alpha 
    R3F leave alpha 
    R3F child leave bravo

    The top-level leave event is on the wrong element: it should be bravo, not alpha. And the out event only comes now. Bravo (the blue cube) never got an over or out event.

    Looking at canvas.tsx, the order is clearly because handlePointerCancel only happens at the end of the event processing, and I think the lack of events on bravo might be because this line is comparing the eventObject instead of the object. Using the eventObject would be correct for enter/leave, where you're not supposed to get events for boundaries inside the element that has the event handler, but you are supposed to get them for over/out, so I think it needs to compare object instead.

    It might be worth noting that I only tried using onPointerOver/Out because of the reverse order of onPointerEnter/Leave. In my application, I need to handle these events by setting a state that contains the currently hovered object, or empty if no object is hovered. First I tried to use an onPointerEnter/Leave handler on the child objects, but then I got a bug with overlapping objects, because the late onPointerLeave would clear my hovered object state after it was set to the new object. I checked the W3C specs for pointer and mouse events and couldn't find anything saying which order they should be in, so I don't know if the behaviour is wrong (from the POV of not matching the specified behaviour of native events), but it's definitely inconvenient this way around. I tried using onPointerOver/Out on my root node instead as a workaround, but then hit this bug.

    Reviewed by dangrabcad at 2020-09-09 12:14
  • 4. Multiple canvases display the same thing

    Hey again, so I'm still trying to replicate this example. From their code, a canvas is created for each couple of images.

    Here is my attempt, also based on the sandbox you provided me in issue #23 (thanks again!) https://codesandbox.io/s/w5902o607

    I'm not sure if it's updating to v1.3.7 that created this but now all canvases are showing the same image, and behave like one. I had it running properly at one point (under v1.3.6) but then I was running into THREE.WebGLRenderer: Context Lost issues.

    Maybe this is not the right place to ask, but it really felt related to the version bump from v1.3.6 to v1.3.7

    Reviewed by dbismut at 2019-03-13 11:15
  • 5. 🔮 v4 collecting ideas (suggestions welcome)

    • [ ] switching on concurrent mode in the reconciler. this will enable time slicing and faster rendering overall. react will be able to schedule and defer updates if it detects that a 60fps framerate is threatened somehow.

    • [ ] optional import components https://github.com/react-spring/react-three-fiber/pull/233 this is more interesting for typescript users.

    import { Mesh } from 'react-three-fiber/components'
    <Mesh />
    • [ ] extras/helpers?

    Things that usually need some boilerplate could be abstracted and collected. For instance controls, level-of-detail, etc

    import { OrbitControls } from 'react-three-fiber/extras'
    <OrbitControls enableDamping />
    • [ ] useCenter?
    import { useCenter } from 'react-three-fiber/extras'
    function useCenter(ref)
      const [center, setCenter] = useState([0, 0, 0])
      const ref = useRef()
      useEffect(() => {
        const box = new Box3().setFromObject(ref.current)
        const sphere = new Sphere()
        setCenter([-sphere.center.x, -sphere.center.y, -sphere.center.z])
      }, [])
      return [ref, center]
    • [ ] something to help with simple animations other than react-spring/three?


    • [ ] new object={...} ?

    currently primitive can project an object into the scene that is already there. if you have a class prototype outside of the THREE namespace you must use extent before you can use it in jsx. what about:

    <new object={Xyz} args={[1,2,3]} {...props} />
    // const temp = new Xyz(1,2,3)
    // applyProps(temp, props)
    Reviewed by drcmda at 2019-11-09 13:03
  • 6. ✍️ Roadmap for 2.x (suggestions welcome)

    • [x] Stable interaction layer so that this lib will be able to interface with react-with-gesture
      • Raytracer order problem: https://github.com/mrdoob/three.js/issues/16031
    • [x] Proper event bubbling, e.stopPropagation()
    • [x] Expose renderloop, so that libs like react-spring do not have to run their own (bad for perf)
    • [x] Allow frameloop invalidation so that it only renders when there's a need for it
    • [x] Stable camera treatment, default-camera rigs, multi-scene rendering, hud, etc.
      • Scene-cam threejs issue: https://github.com/mrdoob/three.js/issues/15929
      • Working impl draft: https://github.com/drcmda/react-three-fiber#heads-up-display-rendering-multiple-scenes
    • [x] Better non-object3d primitive handling
      • Ref problem: https://github.com/facebook/react/pull/15021
      • name="xyz" -> attach="xyz"
      • attachArray="passes" -> allowing objects to fill parent arrays
    • [x] Figure out a definitive set for hooks, currently useThree, useRender
    • [ ] Make auto-canvas-measurement optional
    • [ ] Tests, with help from @bl4ckm0r3
    • [ ] Typescript, with help from @ksjogo
    Reviewed by drcmda at 2019-03-09 10:14
  • 7. Reconciler import fix (revisited)

    This PR reverts the changes from #827 and adds typechecking and linting to the test script (somewhat unrelated to the revert) to ensure that these pass before things get merged to master.

    The changes from #823 to disable esModuleInterop were sensible, as when libraries utilize this any consumer can end up with either:

    1. Errors because they are not using this setting and so the modules will not be resolved
    2. Errors due to referencing a different version of imported libraries than this library

    #827 incorrectly imported the reconciler as a default. It does not have a default export. This can be seen in the tests that are now failing on master here.

      TypeError: react_reconciler_1.default is not a function
          482 | //    NoTimeout
          483 | //  >
        > 484 | const Renderer = Reconciler({
              |                            ^
          485 |   now,
          486 |   createInstance,
          487 |   removeChild,
          at Object.<anonymous> (src/renderer.tsx:484:28)
          at Object.<anonymous> (src/index.ts:1:1)
    Reviewed by JakeSidSmith at 2020-11-23 19:25
  • 8. Can't install react-three-fiber for react-native

    I've try npm install three @react-three/fiber but my code cant run because there's something wrong with the module and stuff, here's the problem. I've searched for the solution but can't find anything that actually works. image

    Here are my dependencies "dependencies": { "@react-three/fiber": "^7.0.4", "expo": "~42.0.1", "expo-status-bar": "~1.0.4", "react": "16.13.1", "react-dom": "16.13.1", "react-native": "https://github.com/expo/react-native/archive/sdk-42.0.0.tar.gz", "react-native-web": "~0.13.12", "three": "^0.130.1" },

    Reviewed by TeamBaconn at 2021-07-10 08:54
  • 9. feat(web): accept function as gl that returns a renderer

    This allows passing in another renderer without having to create a canvas element yourself:

      gl={(canvas) => new WebGPURenderer({ canvas })}

    I do need some advice on how to JavaScript -> TypeScript this bit of code :)

    Reviewed by Tirzono at 2021-10-06 13:54
  • 10. Example for integrating VR Controllers raycasting.

    The code for getting the THREE.js VR controllers working within a react-three-fiber vr experiences seems straightforward, but getting the controllers to trigger click events within the react-three-fiber framework( raycasting ) does not seem obvious.

    It could be helpful to have another example that shows how this can be achieved ( if it is actually currently possible ). It may be another ticket could be needed to enable VR controller raycasting in a proper fashion.

    This codesandbox illustrates an attempt to modify the raycaster. ( NOTE the codesandbox does not work due to XR permissions, but the code is there )


    You can see in the src/components/ControllerRayCaster.tsx component an attempt to modify the raycaster from state using the "useFrame" hook. Here it uses a common pattern from the THREE.js VR demos. Below is a link to an example that updates the origin and direction of a ray used for raycasting:


    Reviewed by gitsome at 2020-04-06 22:31
  • 11. feat(typescript): Basic TypeScript support


    Adds TypeScript support to react-three-fiber by adding a .tsconfig.json, and changing the file extensions to .ts / .tsx. This gives us some decent inferred type-checking out of the box with tsc.

    Also add a new npm script called typecheck, which calls tsc --noEmit --jsx react src/*, to perform static type-checking on the fly, which may be useful if something like CircleCI is setup for continuous integration.

    This is also potentially useful because Babel will compile the files, regardless of if it passes TypeScript type checks or not. 🤷‍♂️

    Example output for yarn run typecheck is as follows:

    src/canvas.tsx:31:19 - error TS2554: Expected 5 arguments, but got 3.
    31       if (camera) applyProps(cam, camera, {})

    @drcmda would love to get your thoughts on the approach, and if you have some ideas on a public types/ interface you would want end-users to use.


    • [ ] Fix existing TypeScript errors from inferred types.
    • [x] Create types/ folder, providing public types for react-three-fiber to be used by end-users.
    Reviewed by setsun at 2019-04-05 04:33
  • 12. fix(native): make loader polyfills tree-shakeable, fix react-native-web resolution

    Makes loaders' polyfills tree-shakeable by injecting them at runtime within Canvas, and will bail if expo modules aren't installed.

    Also fixed an issue with react-native-web where it was consuming native-only modules, so pointed it to web's entrypoint.

    Reviewed by CodyJasonBennett at 2022-05-15 14:08
  • 13. Does not work with react-native

    react-three-fiber depends on react 18 which is not currently supported by react native. if anyone knows a work around or some other alternative please share :)

    Reviewed by LatonDeBasura at 2022-05-11 03:15
  • 14. Suspense causes object to be mounted multiple times

    When using the attach prop with a function, its attach function gets called multiple times without a detach when a suspended sibiling component resumes. This causes potentially too many objects to be created.

    Sandbox: https://codesandbox.io/s/append-bug-xslv50?file=/src/index.tsx

    Reviewed by giulioz at 2022-05-03 10:01
  • 15. onPointerMove get called twice when setPointerCapture is called

    What I'm trying to accomplish is a click and drag "zoom into" effect. I'm using e.point to capture the x and y. Which seems to equal the right values I want. And when used without setPointerCapture everything moves perfectly. When I click I zoom in the camera x,y from the point at which the pointer clicked, set a static z value, and then use onPointerMove to change the x y coordinates. If I drag outside, no more calls happen from onPointerMove (as expected), and then if I let go of the mouse outside of the object onPointerUp doesn't seem to get called. I thought this was a bug but it seems to be expected behaviour.

    That's when I discovered setPointerCapture which seems to fix the onPointerUp problem but has some side effects.

    1. for every onPointerMove call that happens, there seems to be another call that happens right after with some static value (maybe the initial point at which I clicked? I'm not sure.)
    2. When dragging outside the object the onPointerMove gets called even though I'm not over it.

    For a visualization of what I'm going for, here's what I currently had worked on before I introduced onPointerMove: https://krey.ai/music/album/disturbance

    related code:

    const clamp = (num: number, { max, min }: { min: number, max: number }) => Math.min(Math.max(num, min), max);
    function useZoom({ onDrag, onEnd, onStart }: {
      onDrag: (d: Vector3) => void,
      onEnd?: (d: Vector3) => void,
      onStart?: (d: Vector3) => void,
    }) {
      const [active, setActive] = useState(false)
      const down = useCallback(
        (e: ThreeEvent<PointerEvent>) => {
          (e as any).stopPropagation();
          (e as any).target.setPointerCapture((e as any).pointerId);
          if (onStart) onStart(e.point)
      const up = useCallback(
        (e: ThreeEvent<PointerEvent>) => {
          (e as any).stopPropagation();
          (e as any).target.releasePointerCapture((e as any).pointerId);
          if (onEnd) onEnd(e.point)
      const activeRef = useRef<any>()
      useEffect(() => void (activeRef.current = active))
      const move = useCallback(
        (event: ThreeEvent<PointerEvent>) => {
          if (activeRef.current) {
            (event as any).stopPropagation();
      const [bind] = useState(() => ({ onPointerDown: down, onPointerUp: up, onPointerMove: move }))
      return bind
    export const Album = ({ albumName }: { albumName: string }) => {
      const rippleShader = useRippleShader({ albumName });
      const albumMesh = useRef<Mesh>(null);
      const { gl, camera } = useThree();
      const startPosition = { x: 0, y: 0, z: 6 };
      const [_, update] = useSpring(() => ({ ...startPosition, }));
      const changeCamera = useCallback(
        ({ x, y, z }: { x: number, y: number, z: number }) => {
          camera.position.set(x, y, z);
      const clickState = useRef<'UP' | 'DOWN'>('UP')
      const zoom = {
        _formatMovePoint({ x, y }: { x: number; y: number; }) {
          const max = 2;
          const min = -2;
          return {
            x: clamp(x, { max, min }) * 0.8,
            y: clamp(y, { max, min }) * 0.8,
        start(e: Vector3) {
          clickState.current = 'DOWN'
            z: 3,
            onChange: ({ value }) => changeCamera(value),
        stop() {
          clickState.current = 'UP'
          update({ ...startPosition, onChange: ({ value }) => changeCamera(value) });
        move: useCallback((e: Vector3) => {
          if (clickState.current === 'UP') return
            onChange: ({ value }) => changeCamera(value),
        }, [])
      const albumMeshDrag = useZoom({
        onStart: zoom.start,
        onEnd: zoom.stop,
        onDrag: zoom.move
      return (
          onPointerOver={() => {
          onPointerOut={() => {
          <planeBufferGeometry attach="geometry" args={[4, 4]} />
    Reviewed by ryankauk at 2022-05-02 09:01
  • 16. onClick does not work with skinnedMesh

    Hello everyone 👋

    I am facing an issue with adding onClick to skinnedMesh and documentation is not so clear when it comes to raycasting

    Looks like raycast support for skinnedMesh was added in terms of this PR, but I still have no clue how to make it work and everything I found inside documentation is this:

    import { useCamera } from '@react-three/drei'
    return <mesh raycast={useCamera(anotherCamera)} />

    As I understand I have to re-add my skinnedMesh vertices dynamically to the raycaster so it knows where they are right now and can intersect with them, but it is not clear how to do that, documentation says that Fiber does setup pointer events via raycasting on all meshes with onPointer props under the hood during Canvas creation, was thinking that I might need to re-write this part in order to make it work with skinnedMesh, but still not sure

    Any advice is appreciated, thank you 👍

    Reviewed by oleksiikiselov at 2022-05-01 16:47
  • 17. onPointerOver only listens when mouse moves

    If the cursor is stopped and an element enters the contact area, the event is only called when the mouse moves. No "last cursor position" is recorded and there is no recalculation during frames.

    Reviewed by 3GOMESz at 2022-04-30 23:05
Osd-react-renderer - A custom React renderer for OpenSeadragon

osd-react-renderer A custom React renderer for OpenSeadragon viewer, built using

Jan 24, 2022
An experimental, JSX-compatible renderer for the Web Audio API

Wax An experimental, JSX-compatible renderer for the Web Audio API. I wrote Wax for my Manchester Web Meetup talk, Manipulating the Web Audio API with

Mar 22, 2022
React JSX Renderer is a React Component for rendering JSX to React nodes.

React JSX Renderer A React Component for Rendering JSX. Description React JSX Renderer is a React Component for rendering JSX to React nodes.

Jan 9, 2022
[ Unmaintained due to raphamorim/react-ape ] React Renderer for low memory applications
[ Unmaintained due to raphamorim/react-ape ] React Renderer for low memory applications

React-TV · react-tv: React Renderer for low memory applications. react-tv-cli: React Packager for TVs. Currently under development. import React from

May 10, 2022
⚛️ A React renderer for Figma
⚛️ A React renderer for Figma

React Figma A React renderer for Figma. Use React components as a source for your designs. ?? Compatible with react-native, react-sketchapp, react-pri

May 10, 2022
⃝ A react null renderer

Nothing to see here ... Quite so. This package allows you to bring Reacts high-level component abstraction to Node, or wherever you need it. Why not m

Apr 18, 2022
A React renderer for Hardware.

React Hardware React Hardware enables you to build firmata-based hardware applications using a consistent developer experience based on JavaScript and

May 4, 2022
A react renderer for browser's dev console
A react renderer for browser's dev console

Konsul is an abstraction of the browser's console that comes with a React renderer. It offers text styling, images, style inheritance, buttons with cl

Jan 3, 2022
React custom renderer for Appcelerator® Titanium™ SDK
React custom renderer for Appcelerator® Titanium™ SDK

⚠️ Maintainer wanted! Create an issue if you want to maintain it! react-titanium A React custom renderer for Appcelerator® Titanium™ SDK. This rendere

Mar 6, 2022
React renderer for NativeScript
React renderer for NativeScript

React NativeScript is A React renderer for NativeScript, allowing you to write a NativeScript app using the familiar React style. I'm always hanging o

Apr 21, 2022
A react renderer for blessed.

react-blessed A React custom renderer for the blessed library. This renderer should currently be considered as experimental, is subject to change and

May 17, 2022
a react renderer for the command line

This repository is now archived This project was originally created as an alternative to the Ink library, as a separate renderer for a real React comp

Dec 2, 2020
🦍• [Work in Progress] React Renderer to build UI interfaces using canvas/WebGL
🦍• [Work in Progress] React Renderer to build UI interfaces using canvas/WebGL

React Ape React Ape is a react renderer to build UI interfaces using canvas/WebGL. React Ape was built to be an optional React-TV renderer. It's mainl

May 12, 2022
📟 A React Renderer for SSD1306 OLED chip on Raspberry Pi.
📟 A React Renderer for SSD1306 OLED chip on Raspberry Pi.

React SSD1306 A React Renderer for SSD1306 OLED chip on Raspberry Pi For those who doesn't have the device, a canvas-based web emulator is also includ

Apr 19, 2022
React renderer with X11 as a target
React renderer with X11 as a target

react-x11 React custom rendering where side effects are communication with X11 server. The goal is to create a simple library where you would apply yo

Apr 26, 2022
React renderer for building user interfaces in Unity UI

React Unity React Unity is a way to build interactive UI in Unity3D using React. It can be used together with packages like Typescript, redux, i18next

May 16, 2022
A custom Slack renderer for React! 3

react-slack-renderer Render Slack messages in a cool (React) way. const message = SlackRenderer.render( <SlackMessage> <SlackText> Heyo

Jun 22, 2021
🦴 A barebones react renderer for ogl.

react-ogl Declaratively create scenes with re-usable OGL components that have their own state and effects and can tap into React's infinite ecosystem.

Apr 25, 2022
React Fiber Reconciler Renderer for Regl WebGL

react-regl This library enables Regl shader WebGL draw commands to be rendered directly as React components. Demos View demos in the Storybook There i

Apr 13, 2022