Exclude react from bundle, and include it directly from the layout

I’m including React directly from my layout, and I would like webpack to exclude it from the bundle, and make react refers to the window global object React

I’m using the following:

{
        ...
        externals: {
            // Use external version of React
            "react": "React"
        },
        plugins: [
            new webpack.IgnorePlugin(/react/)
        ]
        ...
}

react gets excluded from the bundle, but client side I’m getting Uncaught Error: Cannot find module "react".

Any hints?

Author: Fantashit

14 thoughts on “Exclude react from bundle, and include it directly from the layout

  1. Solution:

    • Create a JS file called dummyReact.js

    • Fill that with these contents:

      module.exports = window.React;

    • Now add an alias in your config.webpack.js

      “react”: “dummyReact.js”

    • Make sure your react is loaded before the WebPack bundle, and this should work.

    In addition, you can also use the ProvidePlugin in conjunction with the dummyReact.js.

  2. Thanks @IngwiePhoenix , the following did work indeed:

    {
        ...
        resolve: {
            extensions: ['', '.js'],
            alias: {
                "react": "dummyReact.js"
            }
        },
        ...
        externals: {
            // Use external version of React
            "react": "React"
        },
        ...
        module: {
            noParse: [ "react" ]
        }
    }
    

    I had to remove new webpack.IgnorePlugin(/react/) for it to work as it seems webpack completely ignores anything that has to do with react.

    My next question, how can I make use of ProvidePlugin in this use case?

  3. Straight from the docs:

    plugin ProvidePlugin
    This plugin makes a module available as variable in every module. The module is required only if you use the variable.

    Example: Make $ and jQuery available in every module without writing require(“jquery”).

    new webpack.ProvidePlugin({
        $: "jquery",
        jQuery: "jquery",
        "window.jQuery": "jquery"
    })

    http://webpack.github.io/docs/shimming-modules.html#order-of-loaders

    I use it exactly like that for jQuery and other modules. Use something like this for React, maybe.

    new webpack.ProvidePlugin({
        React: "React", react: "React", "window.react": "React", "window.React": "React"
    })
    

    ProvidePlugin correctly resolves aliases too. So if you just add the plugin like os, itll “just work” 🙂

  4. If you wonder how exactly this affects your code:

    var $ = require("jquery");
    reuqire("jquery-plugin");
    require("other-plugin");
    
    code();
    
    // VS.
    
    code();

    Combining Alias and Provide, you are able to pretty much reduce the amount of “startup” code you write – effectively saving yourself lines of code (which in turn saves bytes with Uglify).

  5. Alright, so after playing around, my issue was that because ReactDOM was not being excluded, React was being bundled along with ReactDOM.

    I needed to add ReactDOM as an external, in addition to React:

    {
            ...
            externals: {
                // Use external version of React
                "react": "React",
                "react-dom": "ReactDOM"
            },
           ...
    }

    This is applicable since ReactDOM was made a separate dependency when mounting a component (React 0.14.x?).

    I don’t require any dummy-react.js to properly exclude React from my bundle – the externals property is sufficient.

  6. If you want to import your component as a library in another project already importing react instead of having a script tag, you must do this :

      externals: {
        react: 'umd react',
        'react-dom' : 'umd react-dom'
      }
  7. @bebraw thanks for the hint about the analyze tool. I was able to deduce that some of the react addons directly require react. CSSTransitionGroup and TransitionGroup, for example, were the culprits in this case. Changing my externals to this seemed to solve the problem:

    ...
            'react': 'React'
            , 'react-dom': 'ReactDOM'
            , 'react-dom/server': 'ReactDOMServer'
            , 'react/lib/ReactTransitionGroup': 'React.addons.TransitionGroup'
            , 'react/lib/ReactCSSTransitionGroup': 'React.addons.CSSTransitionGroup'
    
  8. So which is it..

      externals: {
        react: 'react',
        'react-dom' : 'react-dom',
      }

    or

     externals: {
        react: 'React',
        'react-dom' : 'ReactDOM',
      }

    Seeing the following in the console.

    There is another module with an equal name when case is ignored.
    This can lead to unexpected behavior when compiling on a filesystem with other case-semantic.
    Rename module if multiple modules are expected or use equal casing if one module is expected.

    Curious because different solutions seem to be working for different folks (presumed via the thumbs up) and I am not understanding why..

  9. Okay so here is the solution that worked for me.. all credits to @Download

    In webpack.config.js you can apparently pass an object..

      externals: {
            react: {
                root: 'React',
                commonjs2: 'react',
                commonjs: 'react',
                amd: 'react'
            },
            'react-dom': {
                root: 'ReactDOM',
                commonjs2: 'react-dom',
                commonjs: 'react-dom',
                amd: 'react-dom'
            }
        }

    Download/react-mdl@755c568

  10. Thanks for the credits @buildbreakdo and glad it’s working for you now.

    Basically what is happening here is that there are different conventions on different systems:

    On the web, libraries normally export to the window object. E.g. react to window.React, react-dom to ReactDOM etc. On Node on the other hand, we require the dependencies by package name.

    Many people that are writing React applications are only using it on the client side. They set output.target to 'web' and only need to list the web method (grab from root) in their externals config. However if you target more than one environment, you need separate externals configs for each environment. React MDL targets the 4 environments listed above.

    Edit:
    For completeness, have a look at the docs on externals. Specifically, the table below it shows for each possible externals type what the resulting import statement looks like. So if you know where it should be coming from, you can work backwards to determine which externals type you need.

  11. Another two cents: If you feel like you’ve done all the right things and it still doesn’t work, maybe it’s actually not your fault but one of your dependencies includes a react addon (e.g. material-ui).
    As they [Facebook] state in a bug report, they’ll ignore the exclusion. But by adding even more webpack configuration you can work around that too:

    externals: {
      ...
      'react-addons-transition-group': {
        commonjs: 'react-addons-transition-group',
        commonjs2: 'react-addons-transition-group',
        amd: 'react-addons-transition-group',
        root: ['React','addons','TransitionGroup']
      }
    }
    
  12. I’m getting the below error when using noParse: ['react'] as noted above:

    Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
     - configuration[0].module.noParse[0]: The provided value "react" is not an absolute path!
       -> An absolute path, when the module starts with this path it is not parsed
    

Comments are closed.