Efficiently Bundle Matter.js with Vite

This guide explains how to optimize Matter.js in a Vite project by eliminating unused modules and reducing your final bundle size. By leveraging named ES module imports, pointing Vite to Matter.js's ESM source, and fine-tuning Rollup's dead-code elimination, you can ensure your production build includes only the physics features your application actually uses.

1. Avoid Default Namespace Imports

The default way many developers use Matter.js is by importing the entire root object:

// Avoid this: pulls the entire library into the bundle
import Matter from 'matter-js';

const engine = Matter.Engine.create();

Importing the default Matter object binds every submodule—including heavyweight modules like Matter.Render, Matter.SVG, and Matter.DomRender—to a single global object, preventing Rollup from tree-shaking them.

Instead, use named ES module imports to pull only the specific physics components your project requires:

// Recommended: allows Rollup to drop unreferenced modules
import { Engine, Composite, Bodies, Runner } from 'matter-js';

const engine = Engine.create();
const runner = Runner.create();

2. Configure Vite to Resolve the ESM Build

By default, some versions of matter-js can resolve to the pre-bundled Universal Module Definition (UMD) file via Node module resolution. UMD builds cannot be tree-shaken effectively.

Update your vite.config.js to explicitly alias matter-js to its modern ESM entry point:

import { defineConfig } from 'vite';

export default defineConfig({
  resolve: {
    alias: {
      'matter-js': 'matter-js/build/matter.esm.js',
    },
  },
  build: {
    rollupOptions: {
      treeshake: {
        preset: 'smallest',
        moduleSideEffects: false,
      },
    },
  },
});

3. Exclude the Built-in Canvas Renderer

The default Render module in Matter.js includes a significant amount of canvas-drawing code, debug visualizers, and event hooks. If you are rendering physics bodies using an external framework (such as Pixi.js, Three.js, or a custom Canvas 2D loop), omit Render completely.

import { Engine, Composite, Bodies, Body } from 'matter-js';

const engine = Engine.create();
const box = Bodies.rectangle(400, 200, 80, 80);

Composite.add(engine.world, box);

// Use a custom game loop instead of Matter.Render
function update() {
  Engine.update(engine, 1000 / 60);
  
  // Render using your custom loop or third-party renderer
  drawBox(box.position.x, box.position.y, box.angle);
  
  requestAnimationFrame(update);
}
requestAnimationFrame(update);

4. Verify Bundle Output

To verify that unused modules (like SVG parsing or debugging tools) are properly eliminated from the production build, use the rollup-plugin-visualizer plugin to inspect your chunks:

npm install --save-dev rollup-plugin-visualizer

Add it to your vite.config.js:

import { defineConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
  plugins: [
    visualizer({
      open: true,
      filename: 'bundle-analysis.html',
    }),
  ],
});

Run vite build. The generated HTML report will show you the exact footprint of matter-js in your output, confirming that only the imported submodules exist in your distribution files.