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-terser2. 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.