Optimizing React apps: Hardcore edition

You’ve heard of minifying. You’ve heard of lazy-loading. You’ve heard of tree shaking. You’ve done it all. Or did you? Here are some optimizations you may have never heard of before. Until now!

Enable “loose” transformations in @babel/preset-…


This content originally appeared on DEV Community and was authored by Wojciech Maj

You've heard of minifying. You've heard of lazy-loading. You've heard of tree shaking. You've done it all. Or did you? Here are some optimizations you may have never heard of before. Until now!

Enable "loose" transformations in @babel/preset-env

Enabling "loose" transformations may make your application considerably smaller. I shaved off roughly 230.9 KB, or 16.2% from my bundle!

This, however, comes at a price: your application may break, both when enabling, and disabling these transformations.

In my case, the only fix I needed to do was related to iterating over HTMLFormControlsCollection (form.elements). I was no longer able to do e.g. [...form.elements], I had to swap it out for Array.from(form.elements).

Still tempted by the large savings? Give it a go by enabling loose flag in Babel config:

babel.config.json

  "presets": [
-   "@babel/preset-env"
+   ["@babel/preset-env", {
+     "loose": true
+   }]
  ]

Remove prop-types from your production bundle

PropTypes are incredibly helpful during development, but they are of no use for your users. You can use babel-plugin-transform-react-remove-prop-types to remove PropTypes from your bundle.

To install, run:

npm install --save-dev babel-plugin-transform-react-remove-prop-types

or

yarn add -D babel-plugin-transform-react-remove-prop-types

and add it to your Babel config like so:

babel.config.json

  "env": {
    "production": {
      "plugins": [
+        "transform-react-remove-prop-types"
      ]
    }
  }

Savings will vary depending on the size of your app. In my case, I shaved off 16.5 KB or about 1.2% from my bundle.

Consider unsafe-wrap mode

unsafe-wrap mode is, as the name states, a bit unsafe for the reasons well explained in plugin's docs.

However, in my case, PropTypes were not accessed from anywhere and the application worked flawlessly.

To enable this mode, you need to change your Babel config like so:

babel.config.json

  "env": {
    "production": {
      "plugins": [
-       "transform-react-remove-prop-types"
+       ["transform-react-remove-prop-types", {
+         "mode": "unsafe-wrap"
+       }]
      ]
    }
  }

This way, I shaved off a total of 35.9 KB or about 2.5% from my bundle.

Enable new JSX transform

Enabling new JSX transform will change the way Babel React preset transpiles JSX to pure JavaScript.

I explained the benefits of enabling it in my other article: How to enable new JSX transform in React 17?.

I highly recommend you to have a read. If that's TL;DR though, all you need to do for quick results is make sure that @babel/core and @babel/preset-env in your project are both on version 7.9.0 or newer, and change your Babel config like so:

babel.config.json

  "presets": [
-   "@babel/preset-react"
+   ["@babel/preset-react", {
+     "runtime": "automatic"
+   }]
  ]

And poof! Roughly 10.5 KB, or 0.7% of my bundle was gone.

Minify your HTML

Chances are your bundler is smart enough to minify JavaScript by default when ran in production mode. But did you know you can minify HTML, too? And JavaScript in that HTML as well?

You're in? Great! Here's what you need to do:

Install html-minifier-terser:

npm install --save-dev html-minifier-terser

or

yarn add -D html-minifier-terser

and change your Webpack config to use it. Define minifier options:

webpack.config.js

const minifyOptions = {
  // Defaults used by HtmlWebpackPlugin
  collapseWhitespace: true,
  removeComments: true,
  removeRedundantAttributes: true,
  removeScriptTypeAttributes: true,
  removeStyleLinkTypeAttributes: true,
  useShortDoctype: true,
  // Custom
  minifyCSS: true,
  minifyJS: true,
};

and use them in HtmlWebpackPlugin…:

webpack.config.js

    new HtmlWebpackPlugin({
+     minify: minifyOptions,
      template: 'index.html',
    }),

…as well as in CopyWebpackPlugin:

webpack.config.js

const { minify } = require('html-minifier-terser');

webpack.config.js

  plugins: [
    new CopyWebpackPlugin({
      patterns: [
        {
          from: 'index.html',
          to: '',
+         transform(content) {
+           return minify(content.toString(), minifyOptions);
+         },
        },
      ]
    }),
  ],

Use babel-plugin-styled-components (styled-components users only)

If you use styled-components, make sure to use their Babel plugin, too. Not only it adds minification of styles, but also adds support for server-side rendering, and provides with a nicer debugging experience.

To install, run:

npm install --save-dev babel-plugin-styled-components

or

yarn add -D babel-plugin-styled-components

and add it to your Babel config like so:

babel.config.json

  "env": {
    "production": {
      "plugins": [
+        "styled-components"
      ]
    }
  }

This will shave off a few kilobytes on its own, but due to added displayNames the savings will not be so apparent just yet. So now…

Disable displayName in production builds

babel.config.json

  "env": {
    "production": {
      "plugins": [
+       ["styled-components", {
+         "displayName": false,
+       }]
      ]
    }
  }

Doing so in my app gave me a surprisingly large savings of 50.4 KB or 3.5%.

Wrap createGlobalStyle contents in css (styled-components users only)

Apparently, while babel-plugin-styled-components is capable of minifying styles, it doesn't minify anything within createGlobalStyle. So, chances are you're shipping your app with tons of unnecessary whitespace.

Remove them by simply wrapping createGlobalStyle contents in css as well, like so:

-const GlobalStyle = createGlobalStyle`
+const GlobalStyle = createGlobalStyle`${css`
   // Your global style goes here
-`;
+`}`;

Replace react-lifecycles-compat with an empty mock

react-lifecycles-compat is a dependency that polyfills lifecycle methods introduced in React 16.3 so that the components polyfilled would work with older React versions. Some dependencies may still use this polyfill in order not to break older React version support.

If you use React 16.3 or newer, you don't need react-lifecycles-compat. You can replace it with a mocked version like so:

__mocks__/reactLifecyclesCompatMock.js

module.exports = {
  polyfill: (Component) => Component,
};

webpack.config.js

  resolve: {
    alias: {
+     'react-lifecycles-compat': path.resolve(__dirname, '__mocks__', 'reactLifecyclesCompatMock.js'),
    },
  },

Doing so will save you 2.5 KB.

Replace classnames with clsx

classnames is not a large dependency, only 729 bytes, but clsx is fully compatible with classnames at just 516 bytes. So, replacing classnames with clsx in your app will save you 213 bytes.

Chances are you'll have both classnames and clsx in your app, e.g. because dependencies may require one or the other. In this case, you can use Webpack's alias to get rid of classnames from your bundle:

webpack.config.js

  resolve: {
    alias: {
+     classnames: 'clsx',
    },
  },

Doing so will save you 729 bytes.

Missing something?

Please share your ideas for not-so-obvious optimizations in the comments below!


This content originally appeared on DEV Community and was authored by Wojciech Maj


Print Share Comment Cite Upload Translate Updates
APA

Wojciech Maj | Sciencx (2021-08-20T15:30:52+00:00) Optimizing React apps: Hardcore edition. Retrieved from https://www.scien.cx/2021/08/20/optimizing-react-apps-hardcore-edition/

MLA
" » Optimizing React apps: Hardcore edition." Wojciech Maj | Sciencx - Friday August 20, 2021, https://www.scien.cx/2021/08/20/optimizing-react-apps-hardcore-edition/
HARVARD
Wojciech Maj | Sciencx Friday August 20, 2021 » Optimizing React apps: Hardcore edition., viewed ,<https://www.scien.cx/2021/08/20/optimizing-react-apps-hardcore-edition/>
VANCOUVER
Wojciech Maj | Sciencx - » Optimizing React apps: Hardcore edition. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/08/20/optimizing-react-apps-hardcore-edition/
CHICAGO
" » Optimizing React apps: Hardcore edition." Wojciech Maj | Sciencx - Accessed . https://www.scien.cx/2021/08/20/optimizing-react-apps-hardcore-edition/
IEEE
" » Optimizing React apps: Hardcore edition." Wojciech Maj | Sciencx [Online]. Available: https://www.scien.cx/2021/08/20/optimizing-react-apps-hardcore-edition/. [Accessed: ]
rf:citation
» Optimizing React apps: Hardcore edition | Wojciech Maj | Sciencx | https://www.scien.cx/2021/08/20/optimizing-react-apps-hardcore-edition/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.