React Three Fiber Physics (Rapier) Quick Start import { Canvas } from '@react-three/fiber' import { Physics, RigidBody, CuboidCollider } from '@react-three/rapier' import { Suspense } from 'react'
function Scene() { return (
{/* Static ground */}
<CuboidCollider position={[0, -2, 0]} args={[10, 0.5, 10]} />
</Physics>
</Suspense>
<ambientLight />
<directionalLight position={[5, 5, 5]} />
</Canvas>
) }
Installation npm install @react-three/rapier
Physics Component
The root component that creates the physics world.
import { Physics } from '@react-three/rapier'
On-Demand Rendering
For performance optimization with static scenes:
RigidBody
Makes objects participate in physics simulation.
Basic Usage import { RigidBody } from '@react-three/rapier'
// Dynamic body (affected by forces/gravity)
// Fixed body (immovable)
// Kinematic body (moved programmatically)
RigidBody Types Type Description dynamic Affected by forces, gravity, collisions (default) fixed Immovable, infinite mass kinematicPosition Moved via setNextKinematicTranslation kinematicVelocity Moved via setNextKinematicRotation RigidBody Properties <RigidBody // Transform position={[0, 5, 0]} rotation={[0, Math.PI / 4, 0]} scale={1}
// Physics type="dynamic" mass={1} restitution={0.5} // Bounciness (0-1) friction={0.5} // Surface friction linearDamping={0} // Slows linear velocity angularDamping={0} // Slows angular velocity gravityScale={1} // Multiplier for gravity
// Collider generation colliders="cuboid" // "cuboid" | "ball" | "hull" | "trimesh" | false
// Constraints lockTranslations={false} // Prevent all translation lockRotations={false} // Prevent all rotation enabledTranslations={[true, true, true]} // Lock specific axes enabledRotations={[true, true, true]} // Lock specific axes
// Sleeping canSleep={true} ccd={false} // Continuous collision detection (fast objects)
// Naming (for collision events) name="player" />
Colliders Automatic Colliders
RigidBody auto-generates colliders from child meshes:
// Global default
// Per-body override
Collider Types Type Description Best For cuboid Box shape Boxes, crates ball Sphere shape Balls, spherical objects hull Convex hull Complex convex shapes trimesh Triangle mesh Concave/complex static geometry Manual Colliders import { CuboidCollider, BallCollider, CapsuleCollider, CylinderCollider, ConeCollider, HeightfieldCollider, TrimeshCollider, ConvexHullCollider } from '@react-three/rapier'
// Standalone collider (static)
// Inside RigidBody (compound collider)
{/ Additional colliders /}
// Collider args reference
Mesh Colliders
For complex shapes:
import { MeshCollider } from '@react-three/rapier'
// Convex hull for dynamic bodies
Applying Forces Using Refs import { RigidBody, RapierRigidBody } from '@react-three/rapier' import { useRef, useEffect } from 'react'
function ForcefulBox() {
const rigidBody = useRef
useEffect(() => { if (rigidBody.current) { // One-time impulse (instantaneous) rigidBody.current.applyImpulse({ x: 0, y: 10, z: 0 }, true)
// Continuous force (apply each frame)
rigidBody.current.addForce({ x: 0, y: 10, z: 0 }, true)
// Torque (rotation)
rigidBody.current.applyTorqueImpulse({ x: 0, y: 5, z: 0 }, true)
rigidBody.current.addTorque({ x: 0, y: 5, z: 0 }, true)
}
}, [])
return (
In useFrame import { useFrame } from '@react-three/fiber'
function ContinuousForce() {
const rigidBody = useRef
useFrame(() => { if (rigidBody.current) { // Apply force every frame rigidBody.current.addForce({ x: 0, y: 20, z: 0 }, true) } })
return (
Getting/Setting Position import { vec3, quat, euler } from '@react-three/rapier'
function PositionControl() {
const rigidBody = useRef
const teleport = () => { if (rigidBody.current) { // Get current transform const position = vec3(rigidBody.current.translation()) const rotation = quat(rigidBody.current.rotation())
// Set new transform
rigidBody.current.setTranslation({ x: 0, y: 10, z: 0 }, true)
rigidBody.current.setRotation({ x: 0, y: 0, z: 0, w: 1 }, true)
// Set velocities
rigidBody.current.setLinvel({ x: 0, y: 0, z: 0 }, true)
rigidBody.current.setAngvel({ x: 0, y: 0, z: 0 }, true)
}
}
return (
Collision Events
On RigidBody
On Colliders
Sensors
Detect overlaps without physical collision:
{/ Invisible sensor trigger /}
// Goal detection example
Collision Groups
Control which objects can collide:
import { interactionGroups } from '@react-three/rapier'
// Group 0, interacts with groups 0, 1, 2
// Group 12, interacts with all groups
// Groups 0 and 5, only interacts with group 7
// On RigidBody (applies to all auto-generated colliders)
Joints
Connect rigid bodies together.
Fixed Joint
Bodies don't move relative to each other:
import { useFixedJoint, RapierRigidBody } from '@react-three/rapier'
function FixedJointExample() {
const bodyA = useRef
useFixedJoint(bodyA, bodyB, [ [0, 0, 0], // Position in bodyA's local space [0, 0, 0, 1], // Orientation in bodyA's local space (quaternion) [0, -1, 0], // Position in bodyB's local space [0, 0, 0, 1], // Orientation in bodyB's local space ])
return (
<>
Revolute Joint (Hinge)
Rotation around one axis:
import { useRevoluteJoint } from '@react-three/rapier'
function HingeDoor() {
const frame = useRef
useRevoluteJoint(frame, door, [ [0.5, 0, 0], // Joint position in frame's local space [-0.5, 0, 0], // Joint position in door's local space [0, 1, 0], // Rotation axis ])
return (
<>
Spherical Joint (Ball-Socket)
Rotation in all directions:
import { useSphericalJoint } from '@react-three/rapier'
function BallJoint() {
const bodyA = useRef
useSphericalJoint(bodyA, bodyB, [ [0, -0.5, 0], // Position in bodyA's local space [0, 0.5, 0], // Position in bodyB's local space ])
return (
<>
Prismatic Joint (Slider)
Translation along one axis:
import { usePrismaticJoint } from '@react-three/rapier'
function Slider() {
const track = useRef
usePrismaticJoint(track, slider, [ [0, 0, 0], // Position in track's local space [0, 0, 0], // Position in slider's local space [1, 0, 0], // Axis of translation ])
return (
<>
Spring Joint
Elastic connection:
import { useSpringJoint } from '@react-three/rapier'
function SpringConnection() {
const anchor = useRef
useSpringJoint(anchor, ball, [ [0, 0, 0], // Position in anchor's local space [0, 0, 0], // Position in ball's local space 2, // Rest length 1000, // Stiffness 10, // Damping ])
return (
<>
Rope Joint
Maximum distance constraint:
import { useRopeJoint } from '@react-three/rapier'
function RopeConnection() {
const anchor = useRef
useRopeJoint(anchor, weight, [ [0, 0, 0], // Position in anchor's local space [0, 0, 0], // Position in weight's local space 3, // Max distance (rope length) ])
return (
<>
Motorized Joints import { useRevoluteJoint } from '@react-three/rapier' import { useFrame } from '@react-three/fiber'
function MotorizedWheel({ bodyA, bodyB }) { const joint = useRevoluteJoint(bodyA, bodyB, [ [0, 0, 0], [0, 0, 0], [0, 0, 1], // Rotation axis ])
useFrame(() => { if (joint.current) { // Configure motor: velocity, damping joint.current.configureMotorVelocity(10, 2) } })
return null }
Instanced Physics
Efficient physics for many identical objects:
import { InstancedRigidBodies, RapierRigidBody } from '@react-three/rapier' import { useRef, useMemo } from 'react'
function InstancedBalls() {
const COUNT = 100
const rigidBodies = useRef
const instances = useMemo(() => {
return Array.from({ length: COUNT }, (_, i) => ({
key: ball-${i},
position: [
(Math.random() - 0.5) * 10,
Math.random() * 10 + 5,
(Math.random() - 0.5) * 10,
] as [number, number, number],
rotation: [0, 0, 0] as [number, number, number],
}))
}, [])
return (
Accessing the World import { useRapier } from '@react-three/rapier' import { useEffect } from 'react'
function WorldAccess() { const { world, rapier } = useRapier()
useEffect(() => { // Change gravity world.setGravity({ x: 0, y: -20, z: 0 })
// Iterate over bodies
world.bodies.forEach((body) => {
console.log(body.translation())
})
}, [world])
return null }
Manual Stepping function ManualStep() { const { step } = useRapier()
const advancePhysics = () => { step(1 / 60) // Advance by one frame }
return }
World Snapshots
Save and restore physics state:
function SnapshotSystem() {
const { world, setWorld, rapier } = useRapier()
const snapshot = useRef
const saveState = () => { snapshot.current = world.takeSnapshot() }
const loadState = () => { if (snapshot.current) { setWorld(rapier.World.restoreSnapshot(snapshot.current)) } }
return ( <> </> ) }
Attractors
From @react-three/rapier-addons:
import { Attractor } from '@react-three/rapier-addons'
// Attract nearby bodies
// Repel bodies
// Selective attraction (only affect certain groups)
Debug Visualization
// Conditional debug
Performance Tips Use appropriate collider types: cuboid and ball are fastest Avoid trimesh for dynamic bodies: Use hull instead Enable sleeping: Bodies at rest stop computing Use collision groups: Reduce collision checks Limit active bodies: Too many dynamic bodies hurts performance Use instanced bodies: For many identical objects Fixed timestep: More stable than variable // Performance-optimized setup <Physics timeStep={1/60} colliders="cuboid" gravity={[0, -9.81, 0]}
{/ Use collision groups to limit checks /}
...
See Also r3f-fundamentals - R3F basics and hooks r3f-interaction - User input and controls r3f-animation - Combining physics with animation