How to Use Matter.js in React Native
Integrating Matter.js into a React Native application enables you to build dynamic 2D physics simulations and interactive games for iOS and Android. This guide explains how to install Matter.js, set up a physics engine alongside a rendering loop, and map physics bodies directly to native React Native UI components.
Core Concepts
Matter.js is purely a physics computation library; it does not
natively render mobile UI elements. In standard web development,
Matter.js typically renders to an HTML5 <canvas>, but
in React Native, you decouple the physics engine from the renderer.
Matter.js calculates positions, velocities, and collisions on each tick,
and React Native components (View,
react-native-svg, or react-native-skia) update
their position and rotation based on those calculations.
Installation
Install Matter.js along with react-native-game-engine,
which provides an optimized loop for updating physics and rendering
frames:
npm install matter-js react-native-game-engineImplementation Steps
1. Set Up the Engine and World
Create a Matter.js engine and populate the world with physical bodies, such as dynamic objects that respond to gravity and static boundaries that act as floors or walls.
import Matter from 'matter-js';
const setupWorld = () => {
const engine = Matter.Engine.create({ enableSleeping: false });
const world = engine.world;
// Create a falling box
const box = Matter.Bodies.rectangle(150, 50, 50, 50, {
restitution: 0.8, // Bounciness
});
// Create a static floor
const floor = Matter.Bodies.rectangle(200, 500, 400, 40, {
isStatic: true,
});
Matter.World.add(world, [box, floor]);
return { engine, world, box, floor };
};2. Create Renderable Components
Build native components to visually represent the Matter.js bodies. These components take the physics body as a prop and use absolute positioning and rotation transforms to match the body's coordinates.
import React from 'react';
import { View } from 'react-native';
export const BoxRenderer = ({ body, size, color }) => {
const { position, angle } = body;
const width = size[0];
const height = size[1];
const x = position.x - width / 2;
const y = position.y - height / 2;
return (
<View
style={{
position: 'absolute',
left: x,
top: y,
width: width,
height: height,
backgroundColor: color,
transform: [{ rotate: `${angle}rad` }],
}}
/>
);
};3. Implement the Update System
A system function runs on every frame tick to advance the physics simulation time.
const PhysicsSystem = (entities, { time }) => {
const engine = entities.physics.engine;
Matter.Engine.update(engine, time.delta);
return entities;
};4. Assemble the GameEngine Component
Combine the physics state, entities, and update systems into the
GameEngine component.
import React, { useRef } from 'react';
import { StyleSheet, View } from 'react-native';
import { GameEngine } from 'react-native-game-engine';
export default function App() {
const { engine, world, box, floor } = setupWorld();
const entities = {
physics: { engine, world },
boxEntity: { body: box, size: [50, 50], color: 'blue', renderer: BoxRenderer },
floorEntity: { body: floor, size: [400, 40], color: 'green', renderer: BoxRenderer },
};
return (
<View style={styles.container}>
<GameEngine
systems={[PhysicsSystem]}
entities={entities}
style={styles.engine}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
engine: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
});Performance Best Practices
- Minimize State Updates: Avoid passing physics
coordinates through React's local state (
useState) on every frame, as this triggers unnecessary re-renders. Let the render loop directly read the body positions. - Keep Shapes Simple: Use standard circles and rectangles whenever possible. Complex concave polygons require polygon decomposition, which increases calculation overhead.
- Use Skia for Complex Visuals: For scenes with
numerous bodies or particle effects, replace standard React Native
Viewcomponents with hardware-accelerated drawing solutions like@shopify/react-native-skia.