Pmndrs.docs

Using with TypeScript

This guide will help through common scenarios and how to approach them with TypeScript.

This tutorial will assume some React and TypeScript knowledge. You can fork and follow along from this starter codesandbox.

Typing with useRef

React's useRef won't automatically infer types despite pointing it to a typed ref.

You can type the ref yourself by passing a type through useRef's generics:

import { useRef, useEffect } from 'react'
import { Mesh } from 'three'

function Box(props) {
  const meshRef = useRef<Mesh>(null!)

  useEffect(() => {
    console.log(Boolean(meshRef.current))
  }, [])

  return (
    <mesh {...props} ref={meshRef}>
      <boxGeometry />
      <meshBasicMaterial />
    </mesh>
  )
}

The exclamation mark is a non-null assertion that will let TS know that ref.current is defined when we access it in effects.

Typing shorthand props

react-three-fiber accepts short-hand props like scalars, strings, and arrays so you can declaratively set properties without side effects.

Here are the different variations of props:

import { Euler, Vector3, Color } from 'three'

rotation: Euler || [x, y, z]
position: Vector3 || [x, y, z] || scalar
color: Color || 'hotpink' || 0xffffff

Each property has extended types which you can pull from to type these properties.

import { Euler, Vector3, Color } from '@react-three/fiber'
// or
// import { ReactThreeFiber } from '@react-three/fiber'
// ReactThreeFiber.Euler, ReactThreeFiber.Vector3, etc.

rotation: Euler
position: Vector3
color: Color

This is particularly useful if you are typing properties outside of components, such as a store or a hook.

Extend usage

react-three-fiber can also accept third-party elements and extend them into its internal catalogue.

import { useRef, useEffect } from 'react'
import { GridHelper } from 'three'
import { extend } from '@react-three/fiber'

// Create our custom element
class CustomElement extends GridHelper {}

// Extend so the reconciler will learn about it
extend({ CustomElement })

The catalogue teaches the underlying reconciler how to create fibers for these elements and treat them within the scene.

You can then declaratively create custom elements with primitives, but TypeScript won't know about them nor their props.

// error: 'customElement' does not exist on type 'JSX.IntrinsicElements'

<customElement />

Node Helpers

react-three-fiber exports helpers that you can use to define different types of nodes. These nodes will type an element that we'll attach to the global JSX namespace.

Node
Object3DNode
BufferGeometryNode
MaterialNode
LightNode

Extending ThreeElements

Since our custom element is an object, we'll use Object3DNode to define it.

import { useRef, useEffect } from 'react'
import { GridHelper } from 'three'
import { extend, Object3DNode } from '@react-three/fiber'

// Create our custom element
class CustomElement extends GridHelper {}

// Extend so the reconciler will learn about it
extend({ CustomElement })

// Add types to ThreeElements elements so primitives pick up on it
declare module '@react-three/fiber' {
  interface ThreeElements {
    customElement: Object3DNode<CustomElement, typeof CustomElement>
  }
}

// react-three-fiber will create your custom component and TypeScript will understand it
<customComponent />

Exported types

react-three-fiber is extensible and exports types for its internals, such as render props, canvas props, and events:

// Event raycaster intersection
Intersection

// `useFrame` internal subscription and render callback
Subscription
RenderCallback

// `useThree`'s returned internal state
RootState
Performance
Dpr
Size
Viewport
Camera

// Canvas props
Props

// Supported events
Events

// Event manager signature (is completely modular)
EventManager

// Wraps a platform event as it's passed through the event manager
ThreeEvent