Skip to content

Minimizing bundle size

Learn more about the tools you can leverage to reduce the bundle size.

Bundle size matters

Material UI's maintainers take bundle size very seriously. Size snapshots are taken on every commit for every package and critical parts of those packages (view the latest snapshot). Combined with dangerJS we can inspect detailed bundle size changes on every Pull Request.

When and how to use tree-shaking?

Tree-shaking Material UI works out of the box in modern frameworks. Material UI exposes its full API on the top-level @mui imports. If you're using ES6 modules and a bundler that supports tree-shaking (webpack >= 2.x, parcel with a flag) you can safely use named imports and still get an optimized bundle size automatically:

<span class="token keyword">import</span> <span class="token punctuation">{</span> Button<span class="token punctuation">,</span> TextField <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@mui/material'</span><span class="token punctuation">;</span>

⚠️ The following instructions are only needed if you want to optimize your development startup times or if you are using an older bundler that doesn't support tree-shaking.

Development environment

Development bundles can contain the full library which can lead to slower startup times. This is especially noticeable if you use named imports from @mui/icons-material, which can be up to six times slower than the default import. For example, between the following two imports, the first (named) can be significantly slower than the second (default):

<span class="token comment">// 🐌 Named</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> Delete <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@mui/icons-material'</span><span class="token punctuation">;</span>
<span class="token comment">// 🚀 Default</span>
<span class="token keyword">import</span> Delete <span class="token keyword">from</span> <span class="token string">'@mui/icons-material/Delete'</span><span class="token punctuation">;</span>

If this is an issue for you, you have two options:

Option one: use path imports

You can use path imports to avoid pulling in unused modules. For instance, use:

<span class="token comment">// 🚀 Fast</span>
<span class="token keyword">import</span> Button <span class="token keyword">from</span> <span class="token string">'@mui/material/Button'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> TextField <span class="token keyword">from</span> <span class="token string">'@mui/material/TextField'</span><span class="token punctuation">;</span>

instead of top-level imports (without a Babel plugin):

<span class="token keyword">import</span> <span class="token punctuation">{</span> Button<span class="token punctuation">,</span> TextField <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@mui/material'</span><span class="token punctuation">;</span>

This is the option we document in all the demos since it requires no configuration. It is encouraged for library authors that are extending the components. Head to Option 2 for the approach that yields the best DX and UX.

While importing directly in this manner doesn't use the exports in the main file of @mui/material, this file can serve as a handy reference as to which modules are public.

Be aware that we only support first and second-level imports. Anything deeper is considered private and can cause issues, such as module duplication in your bundle.

<span class="token comment">// ✅ OK</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> Add <span class="token keyword">as</span> AddIcon <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@mui/icons-material'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> Tabs <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@mui/material'</span><span class="token punctuation">;</span>
<span class="token comment">//                         ^^^^^^^^ 1st or top-level</span>

<span class="token comment">// ✅ OK</span>
<span class="token keyword">import</span> AddIcon <span class="token keyword">from</span> <span class="token string">'@mui/icons-material/Add'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> Tabs <span class="token keyword">from</span> <span class="token string">'@mui/material/Tabs'</span><span class="token punctuation">;</span>
<span class="token comment">//                              ^^^^ 2nd level</span>

<span class="token comment">// ❌ NOT OK</span>
<span class="token keyword">import</span> TabIndicator <span class="token keyword">from</span> <span class="token string">'@mui/material/Tabs/TabIndicator'</span><span class="token punctuation">;</span>
<span class="token comment">//                                           ^^^^^^^^^^^^ 3rd level</span>

If you're using eslint you can catch problematic imports with the no-restricted-imports rule. The following .eslintrc configuration will highlight problematic imports from @mui packages:

<span class="token punctuation">{</span>
  <span class="token property">"rules"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token property">"no-restricted-imports"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
      <span class="token string">"error"</span><span class="token punctuation">,</span>
      <span class="token punctuation">{</span>
        <span class="token property">"patterns"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"@mui/*/*/*"</span><span class="token punctuation">]</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">]</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

Option two: use a Babel plugin

This option provides the best user experience and developer experience:

  • UX: The Babel plugin enables top-level tree-shaking even if your bundler doesn't support it.
  • DX: The Babel plugin makes startup time in dev mode as fast as Option 1.
  • DX: This syntax reduces the duplication of code, requiring only a single import for multiple modules. Overall, the code is easier to read, and you are less likely to make a mistake when importing a new module.
<span class="token keyword">import</span> <span class="token punctuation">{</span> Button<span class="token punctuation">,</span> TextField <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@mui/material'</span><span class="token punctuation">;</span>

However, you need to apply the two following steps correctly.

1. Configure Babel

Pick one of the following plugins:

  • babel-plugin-import with the following configuration:

    yarn add -D babel-plugin-import

    Create a .babelrc.js file in the root directory of your project:

    <span class="token keyword">const</span> plugins <span class="token operator">=</span> <span class="token punctuation">[</span>
      <span class="token punctuation">[</span>
        <span class="token string">'babel-plugin-import'</span><span class="token punctuation">,</span>
        <span class="token punctuation">{</span>
          <span class="token literal-property property">libraryName</span><span class="token operator">:</span> <span class="token string">'@mui/material'</span><span class="token punctuation">,</span>
          <span class="token literal-property property">libraryDirectory</span><span class="token operator">:</span> <span class="token string">''</span><span class="token punctuation">,</span>
          <span class="token literal-property property">camel2DashComponentName</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token string">'core'</span><span class="token punctuation">,</span>
      <span class="token punctuation">]</span><span class="token punctuation">,</span>
      <span class="token punctuation">[</span>
        <span class="token string">'babel-plugin-import'</span><span class="token punctuation">,</span>
        <span class="token punctuation">{</span>
          <span class="token literal-property property">libraryName</span><span class="token operator">:</span> <span class="token string">'@mui/icons-material'</span><span class="token punctuation">,</span>
          <span class="token literal-property property">libraryDirectory</span><span class="token operator">:</span> <span class="token string">''</span><span class="token punctuation">,</span>
          <span class="token literal-property property">camel2DashComponentName</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token string">'icons'</span><span class="token punctuation">,</span>
      <span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">]</span><span class="token punctuation">;</span>
    
    module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span> plugins <span class="token punctuation">}</span><span class="token punctuation">;</span>
    
  • babel-plugin-direct-import with the following configuration:

    yarn add -D babel-plugin-direct-import

    Create a .babelrc.js file in the root directory of your project:

    <span class="token keyword">const</span> plugins <span class="token operator">=</span> <span class="token punctuation">[</span>
      <span class="token punctuation">[</span>
        <span class="token string">'babel-plugin-direct-import'</span><span class="token punctuation">,</span>
        <span class="token punctuation">{</span> <span class="token literal-property property">modules</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'@mui/material'</span><span class="token punctuation">,</span> <span class="token string">'@mui/icons-material'</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">]</span><span class="token punctuation">;</span>
    
    module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span> plugins <span class="token punctuation">}</span><span class="token punctuation">;</span>
    

If you are using Create React App, you will need to use a couple of projects that let you use .babelrc configuration, without ejecting.

yarn add -D react-app-rewired customize-cra

Create a config-overrides.js file in the root directory:

<span class="token comment">/* config-overrides.js */</span>
<span class="token comment">/* eslint-disable react-hooks/rules-of-hooks */</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> useBabelRc<span class="token punctuation">,</span> override <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'customize-cra'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token function">override</span><span class="token punctuation">(</span><span class="token function">useBabelRc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

If you wish, babel-plugin-import can be configured through config-overrides.js instead of .babelrc by using this configuration.

Modify your package.json commands:

<span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line">  "scripts": {
</span></span><span class="token deleted-sign deleted"><span class="token prefix deleted">-</span><span class="token line">    "start": "react-scripts start",
</span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line">    "start": "react-app-rewired start",
</span></span><span class="token deleted-sign deleted"><span class="token prefix deleted">-</span><span class="token line">    "build": "react-scripts build",
</span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line">    "build": "react-app-rewired build",
</span></span><span class="token deleted-sign deleted"><span class="token prefix deleted">-</span><span class="token line">    "test": "react-scripts test",
</span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line">    "test": "react-app-rewired test",
</span></span><span class="token unchanged"><span class="token prefix unchanged"> </span><span class="token line">    "eject": "react-scripts eject"
</span><span class="token prefix unchanged"> </span><span class="token line"> }</span></span>

Enjoy significantly faster start times.

2. Convert all your imports

Finally, you can convert your existing codebase to this option with this top-level-imports codemod. It will perform the following diffs:

<span class="token deleted-sign deleted"><span class="token prefix deleted">-</span><span class="token line">import Button from '@mui/material/Button';
</span><span class="token prefix deleted">-</span><span class="token line">import TextField from '@mui/material/TextField';
</span></span><span class="token inserted-sign inserted"><span class="token prefix inserted">+</span><span class="token line">import { Button, TextField } from '@mui/material';</span></span>

Available bundles

The package published on npm is transpiled, with Babel, to take into account the supported platforms.

⚠️ Developers are strongly discouraged to import from any of the other bundles directly. Otherwise it's not guaranteed that dependencies used also use legacy or modern bundles. Instead, use these bundles at the bundler level with e.g Webpack's resolve.alias:

<span class="token punctuation">{</span>
  <span class="token literal-property property">resolve</span><span class="token operator">:</span> <span class="token punctuation">{</span>
    <span class="token literal-property property">alias</span><span class="token operator">:</span> <span class="token punctuation">{</span>
      <span class="token string-property property">'@mui/base'</span><span class="token operator">:</span> <span class="token string">'@mui/base/legacy'</span><span class="token punctuation">,</span>
      <span class="token string-property property">'@mui/lab'</span><span class="token operator">:</span> <span class="token string">'@mui/lab/legacy'</span><span class="token punctuation">,</span>
      <span class="token string-property property">'@mui/material'</span><span class="token operator">:</span> <span class="token string">'@mui/material/legacy'</span><span class="token punctuation">,</span>
      <span class="token string-property property">'@mui/styled-engine'</span><span class="token operator">:</span> <span class="token string">'@mui/styled-engine/legacy'</span><span class="token punctuation">,</span>
      <span class="token string-property property">'@mui/system'</span><span class="token operator">:</span> <span class="token string">'@mui/system/legacy'</span><span class="token punctuation">,</span>
      <span class="token string-property property">'@mui/utils'</span><span class="token operator">:</span> <span class="token string">'@mui/utils/legacy'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

Modern bundle

The modern bundle can be found under the /modern folder. It targets the latest released versions of evergreen browsers (Chrome, Firefox, Safari, Edge). This can be used to make separate bundles targeting different browsers.

Legacy bundle

If you need to support IE 11 you cannot use the default or modern bundle without transpilation. However, you can use the legacy bundle found under the /legacy folder. You don't need any additional polyfills.