Skip to content

Instantly share code, notes, and snippets.

@bvaughn
Last active November 14, 2024 19:13
Show Gist options
  • Save bvaughn/25e6233aeb1b4f0cdb8d8366e54a3977 to your computer and use it in GitHub Desktop.
Save bvaughn/25e6233aeb1b4f0cdb8d8366e54a3977 to your computer and use it in GitHub Desktop.
How to use profiling in production mode for react-dom

React recently introduced an experimental profiler API. This page gives instructions on how to use this API in a production release of your app.

Table of Contents

Profiling in production

React DOM automatically supports profiling in development mode for v16.5+, but since profiling adds some small additional overhead it is opt-in for production mode. This gist explains how to opt-in.

Create React App v3.2+

Creating a profiling build can be done by specifying an additional --profile flag:

yarn build --profile

npm run build -- --profile

Create React App <= v3.1

Enabling profiling permanently

At the moment, the only way to permanently enable production profiling in CRA apps is to eject. Then you can follow the instructions below and apply these changes to config/webpack.config.prod.js in your app folder.

However, you can also enable profiling temporarily without ejecting.

Enabling profiling temporarily

If you only want to profile the application locally in production mode, you can do this by editing node_modules directly.

Follow the instructions below, and apply them to node_modules/react-scripts/config/webpack.config.prod.js. Then you can run yarn build or npm run build to get a profiling build. Note that your changes would be temporary and will not persist between re-runs of your package manager.

Changing the Webpack config

To enable profiling in production mode, modify Webpack configuration file (config/webpack.config.prod.js) as shown below.

react-dom@^17.0.2 / scheduler@^0.20.2

module.exports = {
  // ...
  resolve: {
    // ...
    alias: {
      // ...
      'react-dom$': 'react-dom/profiling',
    },
    // ...
  },
  // ...
};

Note that if you're using a version of react/react-dom that's less than 16.6, you should refer to this earlier revision of the documentation instead.

Note that if you're using the schedule package v0.3.0-v0.4.0 you should refer to this earlier revision of the documentation instead.

Optional: Disabling mangling for local profiling

When profiling locally, you might want to disable function name mangling so that you can see the component names in the profiler. Note that this will significantly increase your bundle size so only do this during local development! To do this, find the mangle option for UglifyJSPlugin in the config, and set it to false. Don't forget to undo your changes before a real deployment.

Webpack 4

If you are using Webpack 4 to bundle your apps, add the following import aliases to your production config:

react-dom@^17.0.2 / scheduler@^0.20.2

module.exports = {
  //...
  resolve: {
    alias: {
      'react-dom$': 'react-dom/profiling',
    }
  }
};

Note that if you're using a version of react/react-dom that's less than 16.6, you should refer to this earlier revision of the documentation instead.

Note that if you're using the schedule package v0.3.0-v0.4.0 you should refer to this earlier revision of the documentation instead.

Optional: Disabling mangling for local profiling

When profiling locally, you might want to disable function name mangling so that you can see the component names in the profiler. Note that this will significantly increase your bundle size so only do this during local development! To do this, find the mangle option for UglifyJSPlugin in the config, and set it to false. Don't forget to undo your changes before a real deployment.

@athulantony95
Copy link

I am getting the below error for your solution @louiszawadzki

error: index.js: Cannot find module 'babel-plugin-module-resolver'
Require stack:
- app/root/node_modules/metro-react-native-babel-transformer/node_modules/@babel/core/lib/config/files/plugins.js
- app/root/node_modules/metro-react-native-babel-transformer/node_modules/@babel/core/lib/config/files/index.js
- app/root/node_modules/metro-react-native-babel-transformer/node_modules/@babel/core/lib/index.js
- app/root/node_modules/metro-react-native-babel-transformer/src/index.js
- app/root/node_modules/metro-transform-worker/src/index.js
- app/root/node_modules/metro/src/DeltaBundler/Worker.js
- app/root/node_modules/jest-worker/build/workers/processChild.js

@ahayes91
Copy link

@Tymek thanks for the vite snippet! Wondering if you ever saw errors like this during your vite build:

ERROR: [vite:load-fallback] Could not load /Users/aislinn.hayes/code/monorepo/apps/app1/node_modules/react-dom/profiling/test-utils (imported by ../../node_modules/@testing-library/react/dist/@testing-library/react.esm.js): ENOENT: no such file or directory, open '/Users/aislinn.hayes/code/monorepo/apps/app1/node_modules/react-dom/profiling/test-utils'
✘ [ERROR] Could not read from file: /Users/aislinn.hayes/code/monorepo/apps/app1/node_modules/react-dom/profiling

    ../../node_modules/@testing-library/react/dist/@testing-library/react.esm.js:3:21:
      3 │ import ReactDOM from 'react-dom';
        ╵                      ~~~~~~~~~~~

✘ [ERROR] Could not read from file: /Users/aislinn.hayes/code/monorepo/apps/app1/node_modules/react-dom/profiling/test-utils

    ../../node_modules/@testing-library/react/dist/@testing-library/react.esm.js:1:27:
      1 │ import * as testUtils from 'react-dom/test-utils';
        ╵                            ~~~~~~~~~~~~~~~~~~~~~~

✘ [ERROR] Could not read from file: /Users/aislinn.hayes/code/monorepo/apps/app1/node_modules/react-dom/profiling/client

    ../../node_modules/@testing-library/react/dist/@testing-library/react.esm.js:4:32:
      4 │ import * as ReactDOMClient from 'react-dom/client';

I don't know why @testing-library/react would even be trying to build during our production build 🤨
We exclude any files that reference that package from our tsconfig.json AFAIK.
For extra complexity, I am using vite in a monorepo as well 😇

    "react": "18.2.0",
    "react-dom": "18.2.0",
    "vite": "5.1.4",

Any pointers on where to look to try to debug this one? Thank you!

@zeorin
Copy link

zeorin commented Mar 15, 2024

{
  resolve: {
    alias: [
      { find: /^react-dom$/, replacement: 'react-dom/profiling' },
      { find: 'scheduler/tracing', replacement: 'scheduler/tracing-profiling' }
    ]
  }
}

in vite.config.js did the trick for me.

@ThenMorning
Copy link

ThenMorning commented Jun 12, 2024

I am using react-devtools-core to implement a custom backend for debugging a custom renderer similar to React Native. During the debugging process, React is using the development build version:
"react": "18.2.0", "react-devtools-core": "4.28.0", "react-reconciler": "0.29.0"
However, I have found that in react-devtools-core, the recordProfilingDurations method is not capturing accurate durations for the fiber nodes, and all durations are 0. Are there any additional steps required to record the render times (duration) accurately?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment