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-engine

Implementation 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