diff --git a/packages/react-scripts/config/paths.js b/packages/react-scripts/config/paths.js
index e5a3e0b537467cbb0c7bcf48a38e4ef996910573..b59f41e80a64e425b788ef35bcbed6fe75f8cb18 100644
--- a/packages/react-scripts/config/paths.js
+++ b/packages/react-scripts/config/paths.js
@@ -75,6 +75,7 @@ const resolveModule = (resolveFn, filePath) => {
 
 // config after eject: we're in ./config/
 module.exports = {
+  reactConfig: resolveApp('react.config.js'),
   dotenv: resolveApp('.env'),
   appPath: resolveApp('.'),
   appBuild: resolveApp('build'),
@@ -98,6 +99,7 @@ const resolveOwn = relativePath => path.resolve(__dirname, '..', relativePath);
 
 // config before eject: we're in ./node_modules/react-scripts/config/
 module.exports = {
+  reactConfig: resolveApp('react.config.js'),
   dotenv: resolveApp('.env'),
   appPath: resolveApp('.'),
   appBuild: resolveApp('build'),
@@ -133,6 +135,7 @@ if (
   __dirname.indexOf(path.join('packages', 'react-scripts', 'config')) !== -1
 ) {
   module.exports = {
+    reactConfig: resolveApp('react.config.js'),
     dotenv: resolveOwn('template/.env'),
     appPath: resolveApp('.'),
     appBuild: resolveOwn('../../build'),
diff --git a/packages/react-scripts/config/webpack.config.js b/packages/react-scripts/config/webpack.config.js
index 074bb0d6d5fa35641d92027e0553c5c8aaa3938d..8ed2839933df4d974b8ddf619c646b1c62757fec 100644
--- a/packages/react-scripts/config/webpack.config.js
+++ b/packages/react-scripts/config/webpack.config.js
@@ -11,6 +11,7 @@
 const fs = require('fs');
 const path = require('path');
 const webpack = require('webpack');
+const WebpackChain = require('webpack-chain');
 const resolve = require('resolve');
 const PnpWebpackPlugin = require('pnp-webpack-plugin');
 const HtmlWebpackPlugin = require('html-webpack-plugin');
@@ -90,653 +91,741 @@ module.exports = function(webpackEnv) {
   const env = getClientEnvironment(publicUrl);
 
   // common function to get style loaders
-  const getStyleLoaders = (cssOptions, preProcessor) => {
-    const loaders = [
-      isEnvDevelopment && require.resolve('style-loader'),
-      isEnvProduction && {
-        loader: MiniCssExtractPlugin.loader,
-        options: shouldUseRelativeAssetPaths ? { publicPath: '../../' } : {},
+  const injectStyleLoaders = (rule, cssOptions, preProcessor) => {
+    rule.when(
+      isEnvDevelopment,
+      rule => {
+        rule.use('style').loader(require.resolve('style-loader'));
       },
-      {
-        loader: require.resolve('css-loader'),
-        options: cssOptions,
-      },
-      {
-        // Options for PostCSS as we reference these options twice
-        // Adds vendor prefixing based on your specified browser support in
-        // package.json
-        loader: require.resolve('postcss-loader'),
-        options: {
-          // Necessary for external CSS imports to work
-          // https://github.com/facebook/create-react-app/issues/2677
-          ident: 'postcss',
-          plugins: () => [
-            require('postcss-flexbugs-fixes'),
-            require('postcss-preset-env')({
-              autoprefixer: {
-                flexbox: 'no-2009',
-              },
-              stage: 3,
-            }),
-            // Adds PostCSS Normalize as the reset css with default options,
-            // so that it honors browserslist config in package.json
-            // which in turn let's users customize the target behavior as per their needs.
-            postcssNormalize(),
-          ],
+      rule => {
+        rule
+          .use('cssExtract')
+          .loader(MiniCssExtractPlugin.loader)
+          .options(shouldUseRelativeAssetPaths ? { publicPath: '../../' } : {});
+      }
+    );
+
+    rule
+      .use('css')
+      .loader(require.resolve('css-loader'))
+      .options(cssOptions);
+
+    // Options for PostCSS as we reference these options twice
+    // Adds vendor prefixing based on your specified browser support in
+    // package.json
+    rule
+      .use('postcss')
+      .loader(require.resolve('postcss-loader'))
+      .options({
+        // Necessary for external CSS imports to work
+        // https://github.com/facebook/create-react-app/issues/2677
+        ident: 'postcss',
+        plugins: () => [
+          require('postcss-flexbugs-fixes'),
+          require('postcss-preset-env')({
+            autoprefixer: {
+              flexbox: 'no-2009',
+            },
+            stage: 3,
+          }),
+          // Adds PostCSS Normalize as the reset css with default options,
+          // so that it honors browserslist config in package.json
+          // which in turn let's users customize the target behavior as per their needs.
+          postcssNormalize(),
+        ],
+        sourceMap: isEnvProduction && shouldUseSourceMap,
+      });
+
+    rule.when(preProcessor, rule => {
+      rule
+        .use('resolveUrl')
+        .loader(require.resolve('resolve-url-loader'))
+        .options({
           sourceMap: isEnvProduction && shouldUseSourceMap,
-        },
-      },
-    ].filter(Boolean);
-    if (preProcessor) {
-      loaders.push(
-        {
-          loader: require.resolve('resolve-url-loader'),
-          options: {
-            sourceMap: isEnvProduction && shouldUseSourceMap,
-          },
-        },
-        {
-          loader: require.resolve(preProcessor),
-          options: {
-            sourceMap: true,
-          },
-        }
-      );
-    }
-    return loaders;
+        });
+
+      rule
+        .use(preProcessor)
+        .loader(require.resolve(preProcessor))
+        .options({
+          sourceMap: true,
+        });
+    });
   };
 
-  return {
-    mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
+  const config = new WebpackChain();
+
+  // Turn off performance processing because we utilize
+  // our own hints via the FileSizeReporter
+  config.performance
+    .hints(false)
+    .maxEntrypointSize(Infinity)
+    .maxAssetSize(Infinity);
+
+  // Some libraries import Node modules but don't use them in the browser.
+  // Tell Webpack to provide empty mocks for them so importing them works.
+  config.node.merge({
+    module: 'empty',
+    dgram: 'empty',
+    dns: 'mock',
+    fs: 'empty',
+    http2: 'empty',
+    net: 'empty',
+    tls: 'empty',
+    child_process: 'empty',
+  });
+
+  config
+    .mode(webpackEnv)
     // Stop compilation early in production
-    bail: isEnvProduction,
-    devtool: isEnvProduction
-      ? shouldUseSourceMap
-        ? 'source-map'
-        : false
-      : isEnvDevelopment && 'cheap-module-source-map',
-    // These are the "entry points" to our application.
-    // This means they will be the "root" imports that are included in JS bundle.
-    entry: [
-      // Include an alternative client for WebpackDevServer. A client's job is to
-      // connect to WebpackDevServer by a socket and get notified about changes.
-      // When you save a file, the client will either apply hot updates (in case
-      // of CSS changes), or refresh the page (in case of JS changes). When you
-      // make a syntax error, this client will display a syntax error overlay.
-      // Note: instead of the default WebpackDevServer client, we use a custom one
-      // to bring better experience for Create React App users. You can replace
-      // the line below with these two lines if you prefer the stock client:
-      // require.resolve('webpack-dev-server/client') + '?/',
-      // require.resolve('webpack/hot/dev-server'),
-      isEnvDevelopment &&
-        require.resolve('react-dev-utils/webpackHotDevClient'),
-      // Finally, this is your app's code:
-      paths.appIndexJs,
-      // We include the app code last so that if there is a runtime error during
-      // initialization, it doesn't blow up the WebpackDevServer client, and
-      // changing JS code would still trigger a refresh.
-    ].filter(Boolean),
-    output: {
-      // The build folder.
-      path: isEnvProduction ? paths.appBuild : undefined,
-      // Add /* filename */ comments to generated require()s in the output.
-      pathinfo: isEnvDevelopment,
-      // There will be one main bundle, and one file per asynchronous chunk.
-      // In development, it does not produce real files.
-      filename: isEnvProduction
-        ? 'static/js/[name].[contenthash:8].js'
-        : isEnvDevelopment && 'static/js/bundle.js',
-      // TODO: remove this when upgrading to webpack 5
-      futureEmitAssets: true,
-      // There are also additional JS chunk files if you use code splitting.
-      chunkFilename: isEnvProduction
-        ? 'static/js/[name].[contenthash:8].chunk.js'
-        : isEnvDevelopment && 'static/js/[name].chunk.js',
-      // We inferred the "public path" (such as / or /my-project) from homepage.
-      // We use "/" in development.
-      publicPath: publicPath,
-      // Point sourcemap entries to original disk location (format as URL on Windows)
-      devtoolModuleFilenameTemplate: isEnvProduction
-        ? info =>
-            path
-              .relative(paths.appSrc, info.absoluteResourcePath)
-              .replace(/\\/g, '/')
-        : isEnvDevelopment &&
-          (info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
-      // Prevents conflicts when multiple Webpack runtimes (from different apps)
-      // are used on the same page.
-      jsonpFunction: `webpackJsonp${appPackageJson.name}`,
-      // this defaults to 'window', but by setting it to 'this' then
-      // module chunks which are built will work in web workers as well.
-      globalObject: 'this',
-    },
-    optimization: {
-      minimize: isEnvProduction,
-      minimizer: [
-        // This is only used in production mode
-        new TerserPlugin({
-          terserOptions: {
-            parse: {
-              // We want terser to parse ecma 8 code. However, we don't want it
-              // to apply any minification steps that turns valid ecma 5 code
-              // into invalid ecma 5 code. This is why the 'compress' and 'output'
-              // sections only apply transformations that are ecma 5 safe
-              // https://github.com/facebook/create-react-app/pull/4234
-              ecma: 8,
-            },
-            compress: {
-              ecma: 5,
-              warnings: false,
-              // Disabled because of an issue with Uglify breaking seemingly valid code:
-              // https://github.com/facebook/create-react-app/issues/2376
-              // Pending further investigation:
-              // https://github.com/mishoo/UglifyJS2/issues/2011
-              comparisons: false,
-              // Disabled because of an issue with Terser breaking valid code:
-              // https://github.com/facebook/create-react-app/issues/5250
-              // Pending further investigation:
-              // https://github.com/terser-js/terser/issues/120
-              inline: 2,
-            },
-            mangle: {
-              safari10: true,
-            },
-            // Added for profiling in devtools
-            keep_classnames: isEnvProductionProfile,
-            keep_fnames: isEnvProductionProfile,
-            output: {
-              ecma: 5,
-              comments: false,
-              // Turned on because emoji and regex is not minified properly using default
-              // https://github.com/facebook/create-react-app/issues/2488
-              ascii_only: true,
-            },
-          },
-          // Use multi-process parallel running to improve the build speed
-          // Default number of concurrent runs: os.cpus().length - 1
-          parallel: true,
-          // Enable file caching
-          cache: true,
-          sourceMap: shouldUseSourceMap,
-        }),
-        // This is only used in production mode
-        new OptimizeCSSAssetsPlugin({
-          cssProcessorOptions: {
-            parser: safePostCssParser,
-            map: shouldUseSourceMap
-              ? {
-                  // `inline: false` forces the sourcemap to be output into a
-                  // separate file
-                  inline: false,
-                  // `annotation: true` appends the sourceMappingURL to the end of
-                  // the css file, helping the browser find the sourcemap
-                  annotation: true,
-                }
-              : false,
-          },
-        }),
-      ],
-      // Automatically split vendor and commons
-      // https://twitter.com/wSokra/status/969633336732905474
-      // https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
-      splitChunks: {
-        chunks: 'all',
-        name: false,
+    .bail(isEnvProduction)
+    .when(
+      isEnvProduction && shouldUseSourceMap,
+      config => config.devtool('source-map'),
+      config => config.devtool('cheap-module-source-map')
+    );
+
+  // These are the "entry points" to our application.
+  // This means they will be the "root" imports that are included in JS bundle.
+  config
+    .entry('main')
+    // Include an alternative client for WebpackDevServer. A client's job is to
+    // connect to WebpackDevServer by a socket and get notified about changes.
+    // When you save a file, the client will either apply hot updates (in case
+    // of CSS changes), or refresh the page (in case of JS changes). When you
+    // make a syntax error, this client will display a syntax error overlay.
+    // Note: instead of the default WebpackDevServer client, we use a custom one
+    // to bring better experience for Create React App users. You can replace
+    // the line below with these two lines if you prefer the stock client:
+    // require.resolve('webpack-dev-server/client') + '?/',
+    // require.resolve('webpack/hot/dev-server'),
+    .when(isEnvDevelopment, entry =>
+      entry.add(require.resolve('react-dev-utils/webpackHotDevClient'))
+    )
+    // Finally, this is your app's code:
+    // We include the app code last so that if there is a runtime error during
+    // initialization, it doesn't blow up the WebpackDevServer client, and
+    // changing JS code would still trigger a refresh.
+    .add(paths.appIndexJs);
+
+  config.output
+    // The build folder.
+    .path(isEnvProduction ? paths.appBuild : undefined)
+    // Add /* filename */ comments to generated require()s in the output.
+    .pathinfo(isEnvDevelopment)
+    // There will be one main bundle, and one file per asynchronous chunk.
+    // In development, it does not produce real files.
+    .when(
+      isEnvProduction,
+      output => output.filename('static/js/[name].[contenthash:8].js'),
+      output => output.filename('static/js/bundle.js')
+    )
+
+    // FIXME: https://github.com/neutrinojs/webpack-chain/issues/205
+    // TODO: remove this when upgrading to webpack 5
+    // .futureEmitAssets(true)
+
+    // There are also additional JS chunk files if you use code splitting.
+    .when(
+      isEnvProduction,
+      output =>
+        output.chunkFilename('static/js/[name].[contenthash:8].chunk.js'),
+      output => output.chunkFilename('static/js/[name].chunk.js')
+    )
+    // We inferred the "public path" (such as / or /my-project) from homepage.
+    // We use "/" in development.
+    .publicPath(publicPath)
+    // Point sourcemap entries to original disk location (format as URL on Windows)
+    .when(
+      isEnvProduction,
+      output =>
+        output.devtoolModuleFilenameTemplate(info =>
+          path
+            .relative(paths.appSrc, info.absoluteResourcePath)
+            .replace(/\\/g, '/')
+        ),
+      output =>
+        output.devtoolModuleFilenameTemplate(info =>
+          path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')
+        )
+    )
+    // Prevents conflicts when multiple Webpack runtimes (from different apps)
+    // are used on the same page.
+    .jsonpFunction(`webpackJsonp${appPackageJson.name}`)
+    // this defaults to 'window', but by setting it to 'this' then
+    // module chunks which are built will work in web workers as well.
+    .globalObject('this');
+
+  config.optimization
+    .minimize(isEnvProduction)
+    // Keep the runtime chunk separated to enable long term caching
+    // https://twitter.com/wSokra/status/969679223278505985
+    // https://github.com/facebook/create-react-app/issues/5358
+    .runtimeChunk({
+      name: entrypoint => `runtime-${entrypoint.name}`,
+    })
+    // Automatically split vendor and commons
+    // https://twitter.com/wSokra/status/969633336732905474
+    // https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
+    .splitChunks({
+      chunks: 'all',
+      name: false,
+    });
+
+  config.optimization.minimizer('terser').use(TerserPlugin, [
+    {
+      terserOptions: {
+        parse: {
+          // We want terser to parse ecma 8 code. However, we don't want it
+          // to apply any minification steps that turns valid ecma 5 code
+          // into invalid ecma 5 code. This is why the 'compress' and 'output'
+          // sections only apply transformations that are ecma 5 safe
+          // https://github.com/facebook/create-react-app/pull/4234
+          ecma: 8,
+        },
+        compress: {
+          ecma: 5,
+          warnings: false,
+          // Disabled because of an issue with Uglify breaking seemingly valid code:
+          // https://github.com/facebook/create-react-app/issues/2376
+          // Pending further investigation:
+          // https://github.com/mishoo/UglifyJS2/issues/2011
+          comparisons: false,
+          // Disabled because of an issue with Terser breaking valid code:
+          // https://github.com/facebook/create-react-app/issues/5250
+          // Pending further investigation:
+          // https://github.com/terser-js/terser/issues/120
+          inline: 2,
+        },
+        mangle: {
+          safari10: true,
+        },
+        // Added for profiling in devtools
+        keep_classnames: isEnvProductionProfile,
+        keep_fnames: isEnvProductionProfile,
+        output: {
+          ecma: 5,
+          comments: false,
+          // Turned on because emoji and regex is not minified properly using default
+          // https://github.com/facebook/create-react-app/issues/2488
+          ascii_only: true,
+        },
       },
-      // Keep the runtime chunk separated to enable long term caching
-      // https://twitter.com/wSokra/status/969679223278505985
-      // https://github.com/facebook/create-react-app/issues/5358
-      runtimeChunk: {
-        name: entrypoint => `runtime-${entrypoint.name}`,
+      // Use multi-process parallel running to improve the build speed
+      // Default number of concurrent runs: os.cpus().length - 1
+      parallel: true,
+      // Enable file caching
+      cache: true,
+      sourceMap: shouldUseSourceMap,
+    },
+  ]);
+
+  config.optimization.minimizer('optimizeCSS').use(OptimizeCSSAssetsPlugin, [
+    {
+      cssProcessorOptions: {
+        parser: safePostCssParser,
+        map: shouldUseSourceMap
+          ? {
+              // `inline: false` forces the sourcemap to be output into a
+              // separate file
+              inline: false,
+              // `annotation: true` appends the sourceMappingURL to the end of
+              // the css file, helping the browser find the sourcemap
+              annotation: true,
+            }
+          : false,
       },
     },
-    resolve: {
-      // This allows you to set a fallback for where Webpack should look for modules.
-      // We placed these paths second because we want `node_modules` to "win"
-      // if there are any conflicts. This matches Node resolution mechanism.
-      // https://github.com/facebook/create-react-app/issues/253
-      modules: ['node_modules', paths.appNodeModules].concat(
-        modules.additionalModulePaths || []
+  ]);
+
+  // This allows you to set a fallback for where Webpack should look for modules.
+  // We placed these paths second because we want `node_modules` to "win"
+  // if there are any conflicts. This matches Node resolution mechanism.
+  // https://github.com/facebook/create-react-app/issues/253
+  config.resolve.modules.add('node_modules').add(paths.appNodeModules);
+
+  (modules.additionalModulePaths || []).map(modPath =>
+    config.resolve.modules.add(modPath)
+  );
+
+  config.resolve.alias
+    // Support React Native Web
+    // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
+    .set('react-native', 'react-native-web')
+    // Allows for better profiling with ReactDevTools
+    .when(isEnvProductionProfile, alias =>
+      alias.merge({
+        'react-dom$': 'react-dom/profiling',
+        'scheduler/tracing': 'scheduler/tracing-profiling',
+      })
+    )
+    .merge(modules.webpackAliases || {});
+
+  // These are the reasonable defaults supported by the Node ecosystem.
+  // We also include JSX as a common component filename extension to support
+  // some tools, although we do not recommend using it, see:
+  // https://github.com/facebook/create-react-app/issues/290
+  // `web` extension prefixes have been added for better support
+  // for React Native Web.
+  paths.moduleFileExtensions
+    .map(ext => `.${ext}`)
+    .filter(ext => useTypeScript || !ext.includes('ts'))
+    .map(ext => config.resolve.extensions.add(ext));
+
+  // Adds support for installing with Plug'n'Play, leading to faster installs and adding
+  // guards against forgotten dependencies and such.
+  config.resolve.plugin('pnp').use(PnpWebpackPlugin);
+
+  // Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
+  // from the current package.
+  config.resolveLoader.plugin('pnp').use(PnpWebpackPlugin.moduleLoader(module));
+
+  // Prevents users from importing files from outside of src/ (or node_modules/).
+  // This often causes confusion because we only process files within src/ with babel.
+  // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
+  // please link the files into your node_modules/ and let module-resolution kick in.
+  // Make sure your source files are compiled, as they will not be processed in any way.
+  config.resolve
+    .plugin('modulescope')
+    .use(ModuleScopePlugin, [paths.appSrc, [paths.appPackageJson]]);
+
+  // FIXME: https://github.com/neutrinojs/webpack-chain/issues/206
+  // config.module.strictExportPresence(true);
+
+  // Disable require.ensure as it's not a standard language feature.
+  config.module.rule('requireEnsure').parser({ requireEnsure: false });
+
+  // First, run the linter.
+  // It's important to do this before Babel processes the JS.
+  config.module
+    .rule('linter')
+    .test(/\.(js|mjs|jsx|ts|tsx)$/)
+    .include.add(paths.appSrc)
+    .end()
+    .enforce('pre')
+    .use('eslint')
+    .loader(require.resolve('eslint-loader'))
+    .options({
+      cache: true,
+      formatter: require.resolve('react-dev-utils/eslintFormatter'),
+      eslintPath: require.resolve('eslint'),
+      resolvePluginsRelativeTo: __dirname,
+      // @remove-on-eject-begin
+      ignore: process.env.EXTEND_ESLINT === 'true',
+      baseConfig: (() => {
+        // We allow overriding the config only if the env variable is set
+        if (process.env.EXTEND_ESLINT === 'true') {
+          const eslintCli = new eslint.CLIEngine();
+          let eslintConfig;
+          try {
+            eslintConfig = eslintCli.getConfigForFile(paths.appIndexJs);
+          } catch (e) {
+            console.error(e);
+            process.exit(1);
+          }
+          return eslintConfig;
+        } else {
+          return {
+            extends: [require.resolve('eslint-config-react-app')],
+          };
+        }
+      })(),
+      useEslintrc: false,
+      // @remove-on-eject-end
+    });
+
+  // "oneOf" will traverse all following loaders until one will
+  // match the requirements. When no loader matches it will fall
+  // back to the "file" loader at the end of the loader list.
+
+  // "url" loader works like "file" loader except that it embeds assets
+  // smaller than specified limit in bytes as data URLs to avoid requests.
+  // A missing `test` is equivalent to a match.
+  config.module
+    .rule('compiler')
+    .oneOf('url')
+    .test([/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/])
+    .use('url')
+    .loader(require.resolve('url-loader'))
+    .options({
+      limit: imageInlineSizeLimit,
+      name: 'static/media/[name].[hash:8].[ext]',
+    });
+
+  // Process application JS with Babel.
+  // The preset includes JSX, Flow, TypeScript, and some ESnext features.
+  config.module
+    .rule('compiler')
+    .oneOf('babel')
+    .test(/\.(js|mjs|jsx|ts|tsx)$/)
+    .include.add(paths.appSrc)
+    .end()
+    .use('babel')
+    .loader(require.resolve('babel-loader'))
+    .options({
+      customize: require.resolve('babel-preset-react-app/webpack-overrides'),
+      // @remove-on-eject-begin
+      babelrc: false,
+      configFile: false,
+      presets: [require.resolve('babel-preset-react-app')],
+      // Make sure we have a unique cache identifier, erring on the
+      // side of caution.
+      // We remove this when the user ejects because the default
+      // is sane and uses Babel options. Instead of options, we use
+      // the react-scripts and babel-preset-react-app versions.
+      cacheIdentifier: getCacheIdentifier(
+        isEnvProduction ? 'production' : isEnvDevelopment && 'development',
+        [
+          'babel-plugin-named-asset-import',
+          'babel-preset-react-app',
+          'react-dev-utils',
+          'react-scripts',
+        ]
       ),
-      // These are the reasonable defaults supported by the Node ecosystem.
-      // We also include JSX as a common component filename extension to support
-      // some tools, although we do not recommend using it, see:
-      // https://github.com/facebook/create-react-app/issues/290
-      // `web` extension prefixes have been added for better support
-      // for React Native Web.
-      extensions: paths.moduleFileExtensions
-        .map(ext => `.${ext}`)
-        .filter(ext => useTypeScript || !ext.includes('ts')),
-      alias: {
-        // Support React Native Web
-        // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
-        'react-native': 'react-native-web',
-        // Allows for better profiling with ReactDevTools
-        ...(isEnvProductionProfile && {
-          'react-dom$': 'react-dom/profiling',
-          'scheduler/tracing': 'scheduler/tracing-profiling',
-        }),
-        ...(modules.webpackAliases || {}),
-      },
+      // @remove-on-eject-end
       plugins: [
-        // Adds support for installing with Plug'n'Play, leading to faster installs and adding
-        // guards against forgotten dependencies and such.
-        PnpWebpackPlugin,
-        // Prevents users from importing files from outside of src/ (or node_modules/).
-        // This often causes confusion because we only process files within src/ with babel.
-        // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
-        // please link the files into your node_modules/ and let module-resolution kick in.
-        // Make sure your source files are compiled, as they will not be processed in any way.
-        new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
+        [
+          require.resolve('babel-plugin-named-asset-import'),
+          {
+            loaderMap: {
+              svg: {
+                ReactComponent: '@svgr/webpack?-svgo,+titleProp,+ref![path]',
+              },
+            },
+          },
+        ],
       ],
-    },
-    resolveLoader: {
-      plugins: [
-        // Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
-        // from the current package.
-        PnpWebpackPlugin.moduleLoader(module),
+      // This is a feature of `babel-loader` for webpack (not Babel itself).
+      // It enables caching results in ./node_modules/.cache/babel-loader/
+      // directory for faster rebuilds.
+      cacheDirectory: true,
+      // See #6846 for context on why cacheCompression is disabled
+      cacheCompression: false,
+      compact: isEnvProduction,
+    });
+
+  // Process any JS outside of the app with Babel.
+  // Unlike the application JS, we only compile the standard ES features.
+  config.module
+    .rule('compiler')
+    .oneOf('babelDeps')
+    .test(/\.(js|mjs)$/)
+    .exclude.add(/@babel(?:\/|\\{1,2})runtime/)
+    .end()
+    .use('babel')
+    .loader(require.resolve('babel-loader'))
+    .options({
+      babelrc: false,
+      configFile: false,
+      compact: false,
+      presets: [
+        [
+          require.resolve('babel-preset-react-app/dependencies'),
+          { helpers: true },
+        ],
       ],
+      cacheDirectory: true,
+      // See #6846 for context on why cacheCompression is disabled
+      cacheCompression: false,
+      // @remove-on-eject-begin
+      cacheIdentifier: getCacheIdentifier(
+        isEnvProduction ? 'production' : isEnvDevelopment && 'development',
+        [
+          'babel-plugin-named-asset-import',
+          'babel-preset-react-app',
+          'react-dev-utils',
+          'react-scripts',
+        ]
+      ),
+      // @remove-on-eject-end
+      // Babel sourcemaps are needed for debugging into node_modules
+      // code.  Without the options below, debuggers like VSCode
+      // show incorrect code and set breakpoints on the wrong lines.
+      sourceMaps: shouldUseSourceMap,
+      inputSourceMap: shouldUseSourceMap,
+    });
+
+  // Adds support for CSS Modules (https://github.com/css-modules/css-modules)
+  // using the extension .module.css
+  config.module
+    .rule('compiler')
+    .oneOf('cssModules')
+    .test(cssModuleRegex);
+
+  injectStyleLoaders(config.module.rule('compiler').oneOf('cssModules'), {
+    importLoaders: 1,
+    sourceMap: isEnvProduction && shouldUseSourceMap,
+    modules: true,
+    getLocalIdent: getCSSModuleLocalIdent,
+  });
+
+  // "postcss" loader applies autoprefixer to our CSS.
+  // "css" loader resolves paths in CSS and adds assets as dependencies.
+  // "style" loader turns CSS into JS modules that inject <style> tags.
+  // In production, we use MiniCSSExtractPlugin to extract that CSS
+  // to a file, but in development "style" loader enables hot editing
+  // of CSS.
+  // By default we support CSS Modules with the extension .module.css
+  config.module
+    .rule('compiler')
+    .oneOf('css')
+    // Don't consider CSS imports dead code even if the
+    // containing package claims to have no side effects.
+    // Remove this when webpack adds a warning or an error for this.
+    // See https://github.com/webpack/webpack/issues/6571
+    .sideEffects(true)
+    .test(cssRegex);
+
+  injectStyleLoaders(config.module.rule('compiler').oneOf('css'), {
+    importLoaders: 1,
+    sourceMap: isEnvProduction && shouldUseSourceMap,
+  });
+
+  // Adds support for CSS Modules, but using SASS
+  // using the extension .module.scss or .module.sass
+  config.module
+    .rule('compiler')
+    .oneOf('sassModules')
+    .test(sassModuleRegex);
+
+  injectStyleLoaders(
+    config.module.rule('compiler').oneOf('sassModules'),
+    {
+      importLoaders: 2,
+      sourceMap: isEnvProduction && shouldUseSourceMap,
+      modules: true,
+      getLocalIdent: getCSSModuleLocalIdent,
     },
-    module: {
-      strictExportPresence: true,
-      rules: [
-        // Disable require.ensure as it's not a standard language feature.
-        { parser: { requireEnsure: false } },
-
-        // First, run the linter.
-        // It's important to do this before Babel processes the JS.
-        {
-          test: /\.(js|mjs|jsx|ts|tsx)$/,
-          enforce: 'pre',
-          use: [
-            {
-              options: {
-                cache: true,
-                formatter: require.resolve('react-dev-utils/eslintFormatter'),
-                eslintPath: require.resolve('eslint'),
-                resolvePluginsRelativeTo: __dirname,
-                // @remove-on-eject-begin
-                ignore: process.env.EXTEND_ESLINT === 'true',
-                baseConfig: (() => {
-                  // We allow overriding the config only if the env variable is set
-                  if (process.env.EXTEND_ESLINT === 'true') {
-                    const eslintCli = new eslint.CLIEngine();
-                    let eslintConfig;
-                    try {
-                      eslintConfig = eslintCli.getConfigForFile(
-                        paths.appIndexJs
-                      );
-                    } catch (e) {
-                      console.error(e);
-                      process.exit(1);
-                    }
-                    return eslintConfig;
-                  } else {
-                    return {
-                      extends: [require.resolve('eslint-config-react-app')],
-                    };
-                  }
-                })(),
-                useEslintrc: false,
-                // @remove-on-eject-end
-              },
-              loader: require.resolve('eslint-loader'),
-            },
-          ],
-          include: paths.appSrc,
-        },
-        {
-          // "oneOf" will traverse all following loaders until one will
-          // match the requirements. When no loader matches it will fall
-          // back to the "file" loader at the end of the loader list.
-          oneOf: [
-            // "url" loader works like "file" loader except that it embeds assets
-            // smaller than specified limit in bytes as data URLs to avoid requests.
-            // A missing `test` is equivalent to a match.
-            {
-              test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
-              loader: require.resolve('url-loader'),
-              options: {
-                limit: imageInlineSizeLimit,
-                name: 'static/media/[name].[hash:8].[ext]',
-              },
-            },
-            // Process application JS with Babel.
-            // The preset includes JSX, Flow, TypeScript, and some ESnext features.
-            {
-              test: /\.(js|mjs|jsx|ts|tsx)$/,
-              include: paths.appSrc,
-              loader: require.resolve('babel-loader'),
-              options: {
-                customize: require.resolve(
-                  'babel-preset-react-app/webpack-overrides'
-                ),
-                // @remove-on-eject-begin
-                babelrc: false,
-                configFile: false,
-                presets: [require.resolve('babel-preset-react-app')],
-                // Make sure we have a unique cache identifier, erring on the
-                // side of caution.
-                // We remove this when the user ejects because the default
-                // is sane and uses Babel options. Instead of options, we use
-                // the react-scripts and babel-preset-react-app versions.
-                cacheIdentifier: getCacheIdentifier(
-                  isEnvProduction
-                    ? 'production'
-                    : isEnvDevelopment && 'development',
-                  [
-                    'babel-plugin-named-asset-import',
-                    'babel-preset-react-app',
-                    'react-dev-utils',
-                    'react-scripts',
-                  ]
-                ),
-                // @remove-on-eject-end
-                plugins: [
-                  [
-                    require.resolve('babel-plugin-named-asset-import'),
-                    {
-                      loaderMap: {
-                        svg: {
-                          ReactComponent:
-                            '@svgr/webpack?-svgo,+titleProp,+ref![path]',
-                        },
-                      },
-                    },
-                  ],
-                ],
-                // This is a feature of `babel-loader` for webpack (not Babel itself).
-                // It enables caching results in ./node_modules/.cache/babel-loader/
-                // directory for faster rebuilds.
-                cacheDirectory: true,
-                // See #6846 for context on why cacheCompression is disabled
-                cacheCompression: false,
-                compact: isEnvProduction,
-              },
-            },
-            // Process any JS outside of the app with Babel.
-            // Unlike the application JS, we only compile the standard ES features.
-            {
-              test: /\.(js|mjs)$/,
-              exclude: /@babel(?:\/|\\{1,2})runtime/,
-              loader: require.resolve('babel-loader'),
-              options: {
-                babelrc: false,
-                configFile: false,
-                compact: false,
-                presets: [
-                  [
-                    require.resolve('babel-preset-react-app/dependencies'),
-                    { helpers: true },
-                  ],
-                ],
-                cacheDirectory: true,
-                // See #6846 for context on why cacheCompression is disabled
-                cacheCompression: false,
-                // @remove-on-eject-begin
-                cacheIdentifier: getCacheIdentifier(
-                  isEnvProduction
-                    ? 'production'
-                    : isEnvDevelopment && 'development',
-                  [
-                    'babel-plugin-named-asset-import',
-                    'babel-preset-react-app',
-                    'react-dev-utils',
-                    'react-scripts',
-                  ]
-                ),
-                // @remove-on-eject-end
-                // Babel sourcemaps are needed for debugging into node_modules
-                // code.  Without the options below, debuggers like VSCode
-                // show incorrect code and set breakpoints on the wrong lines.
-                sourceMaps: shouldUseSourceMap,
-                inputSourceMap: shouldUseSourceMap,
-              },
-            },
-            // "postcss" loader applies autoprefixer to our CSS.
-            // "css" loader resolves paths in CSS and adds assets as dependencies.
-            // "style" loader turns CSS into JS modules that inject <style> tags.
-            // In production, we use MiniCSSExtractPlugin to extract that CSS
-            // to a file, but in development "style" loader enables hot editing
-            // of CSS.
-            // By default we support CSS Modules with the extension .module.css
-            {
-              test: cssRegex,
-              exclude: cssModuleRegex,
-              use: getStyleLoaders({
-                importLoaders: 1,
-                sourceMap: isEnvProduction && shouldUseSourceMap,
-              }),
-              // Don't consider CSS imports dead code even if the
-              // containing package claims to have no side effects.
-              // Remove this when webpack adds a warning or an error for this.
-              // See https://github.com/webpack/webpack/issues/6571
-              sideEffects: true,
-            },
-            // Adds support for CSS Modules (https://github.com/css-modules/css-modules)
-            // using the extension .module.css
-            {
-              test: cssModuleRegex,
-              use: getStyleLoaders({
-                importLoaders: 1,
-                sourceMap: isEnvProduction && shouldUseSourceMap,
-                modules: true,
-                getLocalIdent: getCSSModuleLocalIdent,
-              }),
-            },
-            // Opt-in support for SASS (using .scss or .sass extensions).
-            // By default we support SASS Modules with the
-            // extensions .module.scss or .module.sass
-            {
-              test: sassRegex,
-              exclude: sassModuleRegex,
-              use: getStyleLoaders(
-                {
-                  importLoaders: 2,
-                  sourceMap: isEnvProduction && shouldUseSourceMap,
-                },
-                'sass-loader'
-              ),
-              // Don't consider CSS imports dead code even if the
-              // containing package claims to have no side effects.
-              // Remove this when webpack adds a warning or an error for this.
-              // See https://github.com/webpack/webpack/issues/6571
-              sideEffects: true,
-            },
-            // Adds support for CSS Modules, but using SASS
-            // using the extension .module.scss or .module.sass
-            {
-              test: sassModuleRegex,
-              use: getStyleLoaders(
-                {
-                  importLoaders: 2,
-                  sourceMap: isEnvProduction && shouldUseSourceMap,
-                  modules: true,
-                  getLocalIdent: getCSSModuleLocalIdent,
-                },
-                'sass-loader'
-              ),
-            },
-            // "file" loader makes sure those assets get served by WebpackDevServer.
-            // When you `import` an asset, you get its (virtual) filename.
-            // In production, they would get copied to the `build` folder.
-            // This loader doesn't use a "test" so it will catch all modules
-            // that fall through the other loaders.
-            {
-              loader: require.resolve('file-loader'),
-              // Exclude `js` files to keep "css" loader working as it injects
-              // its runtime that would otherwise be processed through "file" loader.
-              // Also exclude `html` and `json` extensions so they get processed
-              // by webpacks internal loaders.
-              exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
-              options: {
-                name: 'static/media/[name].[hash:8].[ext]',
-              },
+    'sass-loader'
+  );
+
+  // Opt-in support for SASS (using .scss or .sass extensions).
+  // By default we support SASS Modules with the
+  // extensions .module.scss or .module.sass
+  config.module
+    .rule('compiler')
+    .oneOf('sass')
+    // Don't consider CSS imports dead code even if the
+    // containing package claims to have no side effects.
+    // Remove this when webpack adds a warning or an error for this.
+    // See https://github.com/webpack/webpack/issues/6571
+    .sideEffects(true)
+    .test(sassRegex);
+
+  injectStyleLoaders(
+    config.module.rule('compiler').oneOf('sass'),
+    {
+      importLoaders: 2,
+      sourceMap: isEnvProduction && shouldUseSourceMap,
+    },
+    'sass-loader'
+  );
+
+  // "file" loader makes sure those assets get served by WebpackDevServer.
+  // When you `import` an asset, you get its (virtual) filename.
+  // In production, they would get copied to the `build` folder.
+  // This loader doesn't use a "test" so it will catch all modules
+  // that fall through the other loaders.
+  config.module
+    .rule('compiler')
+    .oneOf('file')
+    // Exclude `js` files to keep "css" loader working as it injects
+    // its runtime that would otherwise be processed through "file" loader.
+    // Also exclude `html` and `json` extensions so they get processed
+    // by webpacks internal loaders.
+    .exclude.merge([/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/])
+    .end()
+    .use('file')
+    .loader(require.resolve('file-loader'))
+    .options({
+      name: 'static/media/[name].[hash:8].[ext]',
+    });
+
+  // ** STOP ** Are you adding a new loader?
+  // Make sure to add the new loader(s) before the "file" loader.
+
+  // Generates an `index.html` file with the <script> injected.
+  config.plugin('html').use(HtmlWebpackPlugin, [
+    Object.assign(
+      {},
+      {
+        inject: true,
+        template: paths.appHtml,
+      },
+      isEnvProduction
+        ? {
+            minify: {
+              removeComments: true,
+              collapseWhitespace: true,
+              removeRedundantAttributes: true,
+              useShortDoctype: true,
+              removeEmptyAttributes: true,
+              removeStyleLinkTypeAttributes: true,
+              keepClosingSlash: true,
+              minifyJS: true,
+              minifyCSS: true,
+              minifyURLs: true,
             },
-            // ** STOP ** Are you adding a new loader?
-            // Make sure to add the new loader(s) before the "file" loader.
-          ],
-        },
-      ],
+          }
+        : undefined
+    ),
+  ]);
+
+  // Inlines the webpack runtime script. This script is too small to warrant
+  // a network request.
+  // https://github.com/facebook/create-react-app/issues/5358
+  config.when(isEnvProduction && shouldInlineRuntimeChunk, () => {
+    config
+      .plugin('inlineRuntimeChunk')
+      .use(InlineChunkHtmlPlugin, [HtmlWebpackPlugin, [/runtime-.+[.]js/]]);
+  });
+
+  // Makes some environment variables available in index.html.
+  // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
+  // <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
+  // In production, it will be an empty string unless you specify "homepage"
+  // in `package.json`, in which case it will be the pathname of that URL.
+  // In development, this will be an empty string.
+  config
+    .plugin('interpolateHtml')
+    .use(InterpolateHtmlPlugin, [HtmlWebpackPlugin, env.raw]);
+
+  // This gives some necessary context to module not found errors, such as
+  // the requesting resource.
+  config.plugin('moduleNotFound').use(ModuleNotFoundPlugin, [paths.appPath]);
+
+  // Makes some environment variables available to the JS code, for example:
+  // if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
+  // It is absolutely essential that NODE_ENV is set to production
+  // during a production build.
+  // Otherwise React will be compiled in the very slow development mode.
+  config
+    .plugin('injectEnvVariables')
+    .use(webpack.DefinePlugin, [env.stringified]);
+
+  // This is necessary to emit hot updates (currently CSS only):
+  config.when(isEnvDevelopment, config => {
+    config
+      .plugin('hotModuleReplacement')
+      .use(webpack.HotModuleReplacementPlugin);
+  });
+
+  // Watcher doesn't work well if you mistype casing in a path so we use
+  // a plugin that prints an error when you attempt to do this.
+  // See https://github.com/facebook/create-react-app/issues/240
+  config.when(isEnvDevelopment, config => {
+    config.plugin('caseSensitivePaths').use(CaseSensitivePathsPlugin);
+  });
+
+  // If you require a missing module and then `npm install` it, you still have
+  // to restart the development server for Webpack to discover it. This plugin
+  // makes the discovery automatic so you don't have to restart.
+  // See https://github.com/facebook/create-react-app/issues/186
+  config.when(isEnvDevelopment, config => {
+    config
+      .plugin('missingNodeModules')
+      .use(WatchMissingNodeModulesPlugin, [paths.appNodeModules]);
+  });
+
+  config.when(isEnvProduction, config => {
+    config.plugin('extractCss').use(MiniCssExtractPlugin, [
+      {
+        // Options similar to the same options in webpackOptions.output
+        // both options are optional
+        filename: 'static/css/[name].[contenthash:8].css',
+        chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
+      },
+    ]);
+  });
+
+  // Generate an asset manifest file with the following content:
+  // - "files" key: Mapping of all asset filenames to their corresponding
+  //   output file so that tools can pick it up without having to parse
+  //   `index.html`
+  // - "entrypoints" key: Array of files which are included in `index.html`,
+  //   can be used to reconstruct the HTML if necessary
+  config.plugin('minifest').use(ManifestPlugin, [
+    {
+      fileName: 'asset-manifest.json',
+      publicPath: publicPath,
+      generate: (seed, files, entrypoints) => {
+        const manifestFiles = files.reduce((manifest, file) => {
+          manifest[file.name] = file.path;
+          return manifest;
+        }, seed);
+        const entrypointFiles = entrypoints.main.filter(
+          fileName => !fileName.endsWith('.map')
+        );
+
+        return {
+          files: manifestFiles,
+          entrypoints: entrypointFiles,
+        };
+      },
     },
-    plugins: [
-      // Generates an `index.html` file with the <script> injected.
-      new HtmlWebpackPlugin(
-        Object.assign(
-          {},
-          {
-            inject: true,
-            template: paths.appHtml,
-          },
-          isEnvProduction
-            ? {
-                minify: {
-                  removeComments: true,
-                  collapseWhitespace: true,
-                  removeRedundantAttributes: true,
-                  useShortDoctype: true,
-                  removeEmptyAttributes: true,
-                  removeStyleLinkTypeAttributes: true,
-                  keepClosingSlash: true,
-                  minifyJS: true,
-                  minifyCSS: true,
-                  minifyURLs: true,
-                },
-              }
-            : undefined
-        )
-      ),
-      // Inlines the webpack runtime script. This script is too small to warrant
-      // a network request.
-      // https://github.com/facebook/create-react-app/issues/5358
-      isEnvProduction &&
-        shouldInlineRuntimeChunk &&
-        new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
-      // Makes some environment variables available in index.html.
-      // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
-      // <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
-      // In production, it will be an empty string unless you specify "homepage"
-      // in `package.json`, in which case it will be the pathname of that URL.
-      // In development, this will be an empty string.
-      new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
-      // This gives some necessary context to module not found errors, such as
-      // the requesting resource.
-      new ModuleNotFoundPlugin(paths.appPath),
-      // Makes some environment variables available to the JS code, for example:
-      // if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
-      // It is absolutely essential that NODE_ENV is set to production
-      // during a production build.
-      // Otherwise React will be compiled in the very slow development mode.
-      new webpack.DefinePlugin(env.stringified),
-      // This is necessary to emit hot updates (currently CSS only):
-      isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
-      // Watcher doesn't work well if you mistype casing in a path so we use
-      // a plugin that prints an error when you attempt to do this.
-      // See https://github.com/facebook/create-react-app/issues/240
-      isEnvDevelopment && new CaseSensitivePathsPlugin(),
-      // If you require a missing module and then `npm install` it, you still have
-      // to restart the development server for Webpack to discover it. This plugin
-      // makes the discovery automatic so you don't have to restart.
-      // See https://github.com/facebook/create-react-app/issues/186
-      isEnvDevelopment &&
-        new WatchMissingNodeModulesPlugin(paths.appNodeModules),
-      isEnvProduction &&
-        new MiniCssExtractPlugin({
-          // Options similar to the same options in webpackOptions.output
-          // both options are optional
-          filename: 'static/css/[name].[contenthash:8].css',
-          chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
-        }),
-      // Generate an asset manifest file with the following content:
-      // - "files" key: Mapping of all asset filenames to their corresponding
-      //   output file so that tools can pick it up without having to parse
-      //   `index.html`
-      // - "entrypoints" key: Array of files which are included in `index.html`,
-      //   can be used to reconstruct the HTML if necessary
-      new ManifestPlugin({
-        fileName: 'asset-manifest.json',
-        publicPath: publicPath,
-        generate: (seed, files, entrypoints) => {
-          const manifestFiles = files.reduce((manifest, file) => {
-            manifest[file.name] = file.path;
-            return manifest;
-          }, seed);
-          const entrypointFiles = entrypoints.main.filter(
-            fileName => !fileName.endsWith('.map')
-          );
+  ]);
 
-          return {
-            files: manifestFiles,
-            entrypoints: entrypointFiles,
-          };
-        },
-      }),
-      // Moment.js is an extremely popular library that bundles large locale files
-      // by default due to how Webpack interprets its code. This is a practical
-      // solution that requires the user to opt into importing specific locales.
-      // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
-      // You can remove this if you don't use Moment.js:
-      new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
-      // Generate a service worker script that will precache, and keep up to date,
-      // the HTML & assets that are part of the Webpack build.
-      isEnvProduction &&
-        new WorkboxWebpackPlugin.GenerateSW({
-          clientsClaim: true,
-          exclude: [/\.map$/, /asset-manifest\.json$/],
-          importWorkboxFrom: 'cdn',
-          navigateFallback: publicUrl + '/index.html',
-          navigateFallbackBlacklist: [
-            // Exclude URLs starting with /_, as they're likely an API call
-            new RegExp('^/_'),
-            // Exclude any URLs whose last part seems to be a file extension
-            // as they're likely a resource and not a SPA route.
-            // URLs containing a "?" character won't be blacklisted as they're likely
-            // a route with query params (e.g. auth callbacks).
-            new RegExp('/[^/?]+\\.[^/]+$'),
-          ],
-        }),
-      // TypeScript type checking
-      useTypeScript &&
-        new ForkTsCheckerWebpackPlugin({
-          typescript: resolve.sync('typescript', {
-            basedir: paths.appNodeModules,
-          }),
-          async: isEnvDevelopment,
-          useTypescriptIncrementalApi: true,
-          checkSyntacticErrors: true,
-          resolveModuleNameModule: process.versions.pnp
-            ? `${__dirname}/pnpTs.js`
-            : undefined,
-          resolveTypeReferenceDirectiveModule: process.versions.pnp
-            ? `${__dirname}/pnpTs.js`
-            : undefined,
-          tsconfig: paths.appTsConfig,
-          reportFiles: [
-            '**',
-            '!**/__tests__/**',
-            '!**/?(*.)(spec|test).*',
-            '!**/src/setupProxy.*',
-            '!**/src/setupTests.*',
-          ],
-          silent: true,
-          // The formatter is invoked directly in WebpackDevServerUtils during development
-          formatter: isEnvProduction ? typescriptFormatter : undefined,
+  // Moment.js is an extremely popular library that bundles large locale files
+  // by default due to how Webpack interprets its code. This is a practical
+  // solution that requires the user to opt into importing specific locales.
+  // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
+  // You can remove this if you don't use Moment.js:
+  config
+    .plugin('ignore')
+    .use(webpack.IgnorePlugin, [/^\.\/locale$/, /moment$/]);
+
+  // Generate a service worker script that will precache, and keep up to date,
+  // the HTML & assets that are part of the Webpack build.
+  config.when(isEnvProduction, config => {
+    config.plugin('serviceWorker').use(WorkboxWebpackPlugin.GenerateSW, [
+      {
+        clientsClaim: true,
+        exclude: [/\.map$/, /asset-manifest\.json$/],
+        importWorkboxFrom: 'cdn',
+        navigateFallback: publicUrl + '/index.html',
+        navigateFallbackBlacklist: [
+          // Exclude URLs starting with /_, as they're likely an API call
+          new RegExp('^/_'),
+          // Exclude any URLs whose last part seems to be a file extension
+          // as they're likely a resource and not a SPA route.
+          // URLs containing a "?" character won't be blacklisted as they're likely
+          // a route with query params (e.g. auth callbacks).
+          new RegExp('/[^/?]+\\.[^/]+$'),
+        ],
+      },
+    ]);
+  });
+
+  // TypeScript type checking
+  config.when(useTypeScript, config => {
+    config.plugin('typescriptCheck').use(ForkTsCheckerWebpackPlugin, [
+      {
+        typescript: resolve.sync('typescript', {
+          basedir: paths.appNodeModules,
         }),
-    ].filter(Boolean),
-    // Some libraries import Node modules but don't use them in the browser.
-    // Tell Webpack to provide empty mocks for them so importing them works.
-    node: {
-      module: 'empty',
-      dgram: 'empty',
-      dns: 'mock',
-      fs: 'empty',
-      http2: 'empty',
-      net: 'empty',
-      tls: 'empty',
-      child_process: 'empty',
-    },
-    // Turn off performance processing because we utilize
-    // our own hints via the FileSizeReporter
-    performance: false,
-  };
+        async: isEnvDevelopment,
+        useTypescriptIncrementalApi: true,
+        checkSyntacticErrors: true,
+        resolveModuleNameModule: process.versions.pnp
+          ? `${__dirname}/pnpTs.js`
+          : undefined,
+        resolveTypeReferenceDirectiveModule: process.versions.pnp
+          ? `${__dirname}/pnpTs.js`
+          : undefined,
+        tsconfig: paths.appTsConfig,
+        reportFiles: [
+          '**',
+          '!**/__tests__/**',
+          '!**/?(*.)(spec|test).*',
+          '!**/src/setupProxy.*',
+          '!**/src/setupTests.*',
+        ],
+        silent: true,
+        // The formatter is invoked directly in WebpackDevServerUtils during development
+        formatter: isEnvProduction ? typescriptFormatter : undefined,
+      },
+    ]);
+  });
+
+  return config;
 };
diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json
index 9d92e63c390208b55830cbb4fcfb5553e0ca90f9..fe8814272840edb1e52c83f154cbcdc3258dcd86 100644
--- a/packages/react-scripts/package.json
+++ b/packages/react-scripts/package.json
@@ -77,6 +77,7 @@
     "ts-pnp": "1.1.4",
     "url-loader": "2.1.0",
     "webpack": "4.41.0",
+    "webpack-chain": "6.0.0",
     "webpack-dev-server": "3.2.1",
     "webpack-manifest-plugin": "2.1.1",
     "workbox-webpack-plugin": "4.3.1"
diff --git a/packages/react-scripts/scripts/build.js b/packages/react-scripts/scripts/build.js
index 5bbc8741136c3b59c2fac267438bc5c2e7ab2fec..7647f0740d2e6dc2dd79f4b6eb3785923a2afa0f 100644
--- a/packages/react-scripts/scripts/build.js
+++ b/packages/react-scripts/scripts/build.js
@@ -43,6 +43,10 @@ const printHostingInstructions = require('react-dev-utils/printHostingInstructio
 const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
 const printBuildError = require('react-dev-utils/printBuildError');
 
+const reactConfig = fs.existsSync(paths.reactConfig)
+  ? require(paths.reactConfig)
+  : {};
+
 const measureFileSizesBeforeBuild =
   FileSizeReporter.measureFileSizesBeforeBuild;
 const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
@@ -60,7 +64,11 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
 }
 
 // Generate configuration
-const config = configFactory('production');
+const chainedConfig = configFactory('production');
+if (reactConfig.webpackOverride) {
+  reactConfig.webpackOverride(chainedConfig);
+}
+const config = chainedConfig.toConfig();
 
 // We require that you explicitly set browsers and do not fall back to
 // browserslist defaults.
diff --git a/packages/react-scripts/scripts/start.js b/packages/react-scripts/scripts/start.js
index 6c2602f04ffc50827ad8b9493b86d79b791f6025..830f495ce85f209ddc8e1368c28728f02e999536 100644
--- a/packages/react-scripts/scripts/start.js
+++ b/packages/react-scripts/scripts/start.js
@@ -48,6 +48,10 @@ const paths = require('../config/paths');
 const configFactory = require('../config/webpack.config');
 const createDevServerConfig = require('../config/webpackDevServer.config');
 
+const reactConfig = fs.existsSync(paths.reactConfig)
+  ? require(paths.reactConfig)
+  : {};
+
 const useYarn = fs.existsSync(paths.yarnLockFile);
 const isInteractive = process.stdout.isTTY;
 
@@ -91,7 +95,12 @@ checkBrowsers(paths.appPath, isInteractive)
       // We have not found a port.
       return;
     }
-    const config = configFactory('development');
+
+    const chainedConfig = configFactory('development');
+    if (reactConfig.webpackOverride) {
+      reactConfig.webpackOverride(chainedConfig);
+    }
+    const config = chainedConfig.toConfig();
     const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
     const appName = require(paths.appPackageJson).name;
     const useTypeScript = fs.existsSync(paths.appTsConfig);