Tree-Shaking Matter.js in Rollup and Webpack

Matter.js is a widely used 2D physics engine for the web, but its default distribution pattern can introduce substantial bloat into production bundles if not configured properly. This article outlines the exact configuration steps required to enable effective tree-shaking for Matter.js in both Rollup and Webpack. By switching to modular imports, declaring module side effects, and tuning minification settings, you can eliminate unused physics submodules and drastically decrease your JavaScript bundle size.

The Challenge with Matter.js and Tree-Shaking

The standard distribution of Matter.js packages the entire library under a single global namespace object (e.g., Matter.Engine, Matter.Bodies). When a bundler encounters namespace objects where methods are attached directly to an exported module, static analysis tools cannot easily determine whether properties are accessed dynamically. As a result, bundlers usually retain the entire library, even if your application only requires a fraction of its modules.

To enable tree-shaking, you must import named exports directly from modern ECMAScript Module (ESM) entries and instruct your bundler to treat unused imports as dead code.

// Prevents effective tree-shaking:
import Matter from 'matter-js';
const engine = Matter.Engine.create();

// Enables effective tree-shaking:
import { Engine, Composite, Bodies } from 'matter-js';
const engine = Engine.create();

Configuring Rollup

Rollup natively performs static analysis for ES modules, but external packages often require explicit instructions to prune unused components.

1. Install Required Plugins

Ensure your Rollup setup includes the node resolution and CommonJS compatibility plugins:

npm install --save-dev @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-terser

2. Configure rollup.config.js

Add the treeshake property to your Rollup configuration and mark matter-js to allow dead-code removal without preserving global side effects.

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import terser from '@rollup/plugin-terser';

export default {
  input: 'src/index.js',
  output: {
    file: 'dist/bundle.js',
    format: 'esm',
    sourcemap: true,
  },
  treeshake: {
    preset: 'recommended',
    moduleSideEffects: (id) => {
      // Allow dead-code elimination within matter-js
      if (id.includes('matter-js')) {
        return false;
      }
      return true;
    },
  },
  plugins: [
    resolve({
      browser: true,
      preferBuiltins: false,
    }),
    commonjs(),
    terser(),
  ],
};

Configuring Webpack

Webpack handles tree-shaking via the usedExports optimization and relies on the sideEffects flag to safely discard unused module branches.

1. Update package.json or Webpack Rules

If the installed version of matter-js does not declare "sideEffects": false in its internal package.json, Webpack preserves unused exports to prevent unintended runtime side effects. You can override this directly in webpack.config.js.

2. Configure webpack.config.js

Configure Webpack in production mode with explicit module rules targeting the library:

const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        include: /node_modules\/matter-js/,
        sideEffects: false, // Informs Webpack that Matter.js does not execute top-level side effects
      },
    ],
  },
  optimization: {
    usedExports: true,
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            dead_code: true,
            unused: true,
          },
        },
      }),
    ],
  },
};

Verification

After building your project, inspect the output bundle using visual analysis tools such as rollup-plugin-visualizer for Rollup or webpack-bundle-analyzer for Webpack. Verify that excluded submodules (such as unused constraint solvers, SVG renderers, or manipulation plugins) are no longer present in the output artifacts.