# How to Optimize Your React Build Process to Reduce Bundle Size for Production

> Optimize your React build process and reduce bundle size for production. Learn to use NODE_ENV production, Google Closure Compiler, and production bundle types for smaller, faster deployments.

- Repository: [Meta/react](https://github.com/facebook/react)
- Tags: performance
- Published: 2026-02-16

---

**Set `NODE_ENV=production`, enable the Google Closure Compiler, and use production bundle types to strip development-only code and minify React error messages into numeric codes.**

Optimizing your React build process for production deployment requires understanding how the core library handles dead-code elimination and minification. The `facebook/react` repository implements several aggressive optimization strategies—production bundle types, Closure Compiler integration, and error-code transformation—that you can replicate in your own build pipeline to significantly reduce final bundle size.

## Use Production Bundle Types

React's build system defines specific **production bundle types** that automatically exclude development-only code. When you configure your build to use types like `NODE_PROD`, `ESM_PROD`, or `BROWSER_SCRIPT`, the bundler sets `process.env.NODE_ENV === "production"` and strips out warning messages, PropTypes checks, and other dev-mode utilities.

In [`scripts/rollup/bundles.js`](https://github.com/facebook/react/blob/main/scripts/rollup/bundles.js), the filename conventions for production bundles are defined between lines 1311-1319, ensuring files like `*.production.js` receive the correct optimization flags.

## Enable the Closure Compiler

The React team uses the **Google Closure Compiler** in `SIMPLE` mode to achieve aggressive dead-code elimination and superior symbol renaming compared to standard minifiers like Terser. This step runs after Babel transpilation and before final output generation.

The build orchestration in [`scripts/rollup/build.js`](https://github.com/facebook/react/blob/main/scripts/rollup/build.js) (lines 458-466) inserts the Closure step for production builds. The actual plugin implementation lives in [`scripts/rollup/plugins/closure-plugin.js`](https://github.com/facebook/react/blob/main/scripts/rollup/plugins/closure-plugin.js), which wraps the compiler and feeds it the intermediate bundle code.

## Minify React Error Messages

React replaces full error message strings with **tiny numeric error codes** in production builds, saving approximately 2 KB per error message. Instead of including the full text of "Cannot update during an existing state transition", the bundle contains only a reference like `r(185)`.

This transformation is controlled by `bundle.minifyWithProdErrorCodes` in the Babel configuration. In [`scripts/rollup/build.js`](https://github.com/facebook/react/blob/main/scripts/rollup/build.js) (lines 165-169), production builds enable this by default. The transformation logic resides in [`error-codes/transform-error-messages.js`](https://github.com/facebook/react/blob/main/error-codes/transform-error-messages.js).

## Leverage Tree-Shaking

React's internal modules are written as **ES Modules (ESM)**, allowing modern bundlers to perform tree-shaking and eliminate unused exports. If you import only `useState` and `useEffect` rather than the entire React namespace, the bundler can exclude `Component`, `PureComponent`, and other unused features.

This architectural change is documented in [`CHANGELOG.md`](https://github.com/facebook/react/blob/main/CHANGELOG.md) (lines 408-410), noting the addition of tree-shaking support in React 18.

## Set NODE_ENV to Production

React code contains numerous guards like `if (process.env.NODE_ENV !== "production")` that wrap development warnings and debugging utilities. When you set `NODE_ENV=production` at build time, minifiers can evaluate these conditions at compile time and remove the entire dead branch.

You can see an example of this guard pattern in [`scripts/rollup/wrappers.js`](https://github.com/facebook/react/blob/main/scripts/rollup/wrappers.js) (line 90). Ensure your build tool defines this environment variable to maximize dead-code elimination.

## Build Configuration Examples

### Using the React Repository Rollup Command

If you are building React from source or modeling your build after the official repository, use the built-in Rollup scripts with production flags:

```bash

# Build the production UMD bundle for the web

node scripts/rollup/build.js --type=browser-script --minify

```

This command selects the `BROWSER_SCRIPT` bundle type defined in [`scripts/rollup/bundles.js`](https://github.com/facebook/react/blob/main/scripts/rollup/bundles.js) (lines 1335-1339), applies the Closure plugin, and outputs [`react.production.min.js`](https://github.com/facebook/react/blob/main/react.production.min.js).

### Custom Rollup Configuration

To replicate the React team's optimization pipeline in your own project:

```javascript
// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';
import babel from '@rollup/plugin-babel';
import closure from './scripts/rollup/plugins/closure-plugin.js';
import replace from '@rollup/plugin-replace';

export default {
  input: 'packages/react/src/React.js',
  output: {
    file: 'dist/react.production.min.js',
    format: 'umd',
    name: 'React',
  },
  plugins: [
    resolve(),
    replace({
      'process.env.NODE_ENV': JSON.stringify('production'),
      preventAssignment: true,
    }),
    babel({
      // Production: strip warnings, use error-code transformer
      plugins: [require('../error-codes/transform-error-messages')],
      babelHelpers: 'bundled',
    }),
    closure({ compilation_level: 'SIMPLE' }), // same as internal plugin
  ],
};

```

### Webpack Configuration

For modern React applications using Webpack:

```javascript
// webpack.config.js
module.exports = {
  mode: 'production',            // sets NODE_ENV and enables minification
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    library: 'MyApp',
    libraryTarget: 'umd',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            plugins: [require('../error-codes/transform-error-messages')],
          },
        },
      },
    ],
  },
  optimization: {
    usedExports: true,           // enables tree-shaking
    minimizer: [
      // Optionally add Closure after Terser
      new (require('google-closure-compiler-webpack-plugin'))({
        compilation_level: 'SIMPLE',
      }),
    ],
  },
};

```

### Using Pre-built Production UMD

If you do not need a custom build, import the already-optimized files directly:

```html
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>

```

These files from [`packages/react/README.md`](https://github.com/facebook/react/blob/main/packages/react/README.md) (lines 7-8) are pre-minified and tree-shaken, requiring no build configuration.

## Summary

- **Use production bundle types** (`NODE_PROD`, `ESM_PROD`, `BROWSER_SCRIPT`) to strip development-only code via [`scripts/rollup/bundles.js`](https://github.com/facebook/react/blob/main/scripts/rollup/bundles.js).
- **Enable the Google Closure Compiler** in `SIMPLE` mode through [`scripts/rollup/build.js`](https://github.com/facebook/react/blob/main/scripts/rollup/build.js) and [`scripts/rollup/plugins/closure-plugin.js`](https://github.com/facebook/react/blob/main/scripts/rollup/plugins/closure-plugin.js) for aggressive dead-code elimination.
- **Minify error messages** using the numeric code transformer in [`error-codes/transform-error-messages.js`](https://github.com/facebook/react/blob/main/error-codes/transform-error-messages.js), enabled by default in production builds at [`scripts/rollup/build.js`](https://github.com/facebook/react/blob/main/scripts/rollup/build.js) lines 165-169.
- **Leverage tree-shaking** by importing ES modules, allowing bundlers to eliminate unused exports as documented in [`CHANGELOG.md`](https://github.com/facebook/react/blob/main/CHANGELOG.md).
- **Set `NODE_ENV=production`** to remove development guards found in [`scripts/rollup/wrappers.js`](https://github.com/facebook/react/blob/main/scripts/rollup/wrappers.js) and throughout the codebase.

## Frequently Asked Questions

### What is the difference between development and production React bundles?

Development bundles include full error messages, PropTypes validation, and warning guards to help debug issues during development. Production bundles, generated using types like `NODE_PROD` or `ESM_PROD` in [`scripts/rollup/bundles.js`](https://github.com/facebook/react/blob/main/scripts/rollup/bundles.js), strip these development-only features and replace error strings with numeric codes to minimize file size.

### How much smaller is a production React bundle compared to development?

A production bundle is typically **50% smaller** or more compared to its development counterpart. The reduction comes from removing warning code via `process.env.NODE_ENV` checks, minifying error messages into numeric codes (saving ~2 KB per message), and running the Google Closure Compiler in `SIMPLE` mode as implemented in [`scripts/rollup/build.js`](https://github.com/facebook/react/blob/main/scripts/rollup/build.js).

### Can I use Create React App to optimize my build automatically?

Yes, Create React App (CRA) automatically sets `NODE_ENV=production` and enables minification when you run `npm run build`. However, to achieve the same level of optimization as the core React repository—including error-code minification and Closure Compiler integration—you may need to eject or customize the build configuration to include the `transform-error-messages` Babel plugin and Closure Compiler steps.

### What is tree-shaking and how does it reduce bundle size?

**Tree-shaking** is a dead-code elimination technique where the bundler analyzes ES module import/export statements to remove unused code. React supports tree-shaking as of version 18, as noted in [`CHANGELOG.md`](https://github.com/facebook/react/blob/main/CHANGELOG.md) (lines 408-410). By importing only the specific functions you need—such as `import { useState } from 'react'` rather than the entire namespace—the bundler excludes unused features like `Component` or `PureComponent` from the final output.