18

This is similar to this question however neither of the answers solves the problem.

After running npm run build the resultant index.html looks similar to:

<script>!function (i) { function e(e) { for //rest omitted
<script src="/static/js/2.3f294f32.chunk.js"></script>
<script src="/static/js/main.7b9daa35.chunk.js"></script>

The first <script> element is inlined javascript that i have extracted to a file called loader.js

<script src="/loader.js"></script>
<script src="/static/js/2.3f294f32.chunk.js"></script>
<script src="/static/js/main.7b9daa35.chunk.js"></script>

this works but I would like to combine all 3 files into a single file

I've tried filesmerge.com to combine the JS files but this results in an error when referencing the single file:

output.min.js:1 Uncaught TypeError: (intermediate value)(...) is not a function
at output.min.js:1

I then tried combining using jscompress.com and whilst this does not produce any errors the react root element is not rendered

I've also tried this solution suggested on the create-react-app repo which does not work. No error is produced but no react element is rendered (page remains blank)

wal
  • 17,409
  • 8
  • 74
  • 109

3 Answers3

27

Brief

In short: It's possible, but not very practical. Why? Your application will no longer be performant as your single bundle file grows. A single large request, instead of smaller requests, will inevitably lead to slower web performance and potentially wasted bandwidth.

On that same note, I'd highly advise against using the CRA for your single-bundled application. While the CRA is a great boilerplate geared toward a DX friendly approach to React with Webpack, it does contain a lot of dependencies that may be unnecessarily bundled with your app.

As such, I'd highly recommend building your own Webpack configuration (it's relatively simple with the help of the Webpack documentation combined with the CRA Webpack notes) or consider alternatives like rollup, gulp, microbundle, or browserify to name a few.

The following procedure below will inevitably become outdated as the CRA gets updated. Therefore, use these instructions at your own risk.

Procedure

CRA Version: v4.0.3

You'll first want to eject: yarn eject or npm run eject -- you can probably use some 3rd party packages to override without ejecting, but I'll leave that up to you to figure out.

Then, you'll need to go to the config/webpack.config.js file and change the following:

  • Remove the InlineChunkHtmlPlugin import from top imports and under plugins, remove isEnvProduction && shouldInlineRuntimeChunk && new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]) as it creates a chunk file list inside the index.html file when building
  • Under plugins, change MiniCssExtractPlugin options to only output a single css file by changing filename to filename: "static/css/bundle.min.css" and removing the chunkFileName option.
  • Under output, change filename to filename: "static/js/bundle.min.js" to output to a single filename for production.
  • Under output, remove the chunkFilename property as you're no longer chunking JS files
  • Under optimization, remove splitChunks property as you're no longer splitting JS chunks
  • Under optimization, set runtimeChunk to runtimeChunk: false to avoid creating a runtime.chunk.js file
  • Under optimization, after the TerserPlugin, add new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }) to limit outputted chunks to 1

Demo

CRA Version: v4.0.3 (demo updated as May 25th, 2021)

Working repo: https://github.com/mattcarlotta/cra-single-bundle

Notes

This configuration will inevitably become outdated as the dev world adopts Webpack 5

Matt Carlotta
  • 18,972
  • 4
  • 39
  • 51
  • 4
    that was certainly non-trivial - i'm doing this so i can make a widget that can be easily added to 3rd party websites. just a few notes; there was no `optimizations` section in my `webpack.config.js` (did you mean `optimization`?) however there were dedicated `splitChunks` and `runtimeChunk` fields which i removed - for the diff see here https://www.diffchecker.com/7a1Hdg3a thanks for your post that is some A+ code and instructions – wal Dec 14 '19 at 05:34
  • Ah, good catch, spelling mistake... should be `optimization`. Updated answer. – Matt Carlotta Dec 14 '19 at 05:45
  • 1
    If I wanted to bundle the CSS and JS together (working on a react widget), how would I do that? – Luis Serrano Aug 20 '20 at 08:19
  • Not familiar with widgets. That said, if you're talking about a single bundle file that includes both JS and the CSS, then it's not possible. The JS and the CSS would need to be requested/imported within the `head` or within a React component. If you're looking for a single bundle file, then you'd want to use a CSS-in-JS solution (like `styled-components`, `styled-jsx` or `react-jss` to name a few) which apply `style` tags to classes during run-time. Also, you may want consider using [rollup.js](https://rollupjs.org/guide/en/) instead of Webpack as it's more suitable to an NPM library. – Matt Carlotta Aug 20 '20 at 16:44
  • Thanks @MattCarlotta, solid answer. I've given this a shot and it works great except for one thing. I'm specifying only one .js file, but I'm getting two: `file.min.js` and `1.file.min.js`. – Kevmon Nov 29 '20 at 05:43
  • Updated answer with new instructions and updated demo for [CRA v4.0.3](https://github.com/facebook/create-react-app/releases/tag/v4.0.3) – Matt Carlotta May 25 '21 at 21:41
  • 1
    @LuisSerrano See my answer [here](https://stackoverflow.com/a/71683116/1107110). – Drazen Bjelovuk Mar 30 '22 at 19:37
5

Matt's solution is the closest one I got by now. After a few hours of searching, I found a way to force Webpack to output one js file. Just put it here for future reference or anyone gets this problem.

plugins: [
      isEnvProduction &&
      new webpack.optimize.LimitChunkCountPlugin({
        maxChunks: 1
      }),
...
]
Yao
  • 88
  • 1
  • 4
3

You can do it more easy:

  1. Add package yarn add react-app-rewired
  2. Change scrips section in package.json
"scripts": {
   "start": "react-app-rewired start",
   "build": "react-app-rewired build",
   "test": "react-app-rewired test",
   "eject": "react-scripts eject"
}
  1. create the file config-overrides.js
module.exports = {
    webpack: function(config, env) {
        config.optimization.splitChunks = {
            cacheGroups: {
               default: false
            }
        };
        config.optimization.runtimeChunk = false;
        return config;
    }
}

From here: https://github.com/facebook/create-react-app/issues/5306#issuecomment-431431877

mm_
  • 1,566
  • 2
  • 18
  • 37
arch
  • 31
  • 2