Is it possible to load webpack externals without using a script tag?

458 Views Asked by At

Scenario:

I would like to embed React components in a web page as isolated react apps. As an example, lets say a date picker and also a colour picker.

I don't want to bundle React, or other common dependencies twice (or as many times as I end up using this approach) for each isolated bundle. I understand that I can use webpack externals to achieve this.

Why using webpack externals is not ideal

webpack externals means loading one version of React via a script tag and making it available as a global object. For the lifetime of the website I'm working on, I'd be stuck:

  • Using one version of React
  • Painfully updating all of the components if I want to use a new version

Ideal solution

Webpack bundles the individual applications into their own scope, if I package up different React versions without making them external it appears to work without issue as each instance is isolated.

Is there some way of configuring webpack to look for an external module (e.g. a specific version of React from a CDN) at runtime but crucially not attach it to the global namespace and load it within the scope of the bundled code?

The major benefit would be allowing React to be cached by the browser and avoiding adding the same download footprint every time I build a new component.

Thanks for your time and consideration.

1

There are 1 best solutions below

0
On

Not 100% sure this is what you want but I am doing something similar:

I've got multiple webpack entries in a single config which render different react components. I then split out the common dependency react into a different chunk. This means that there are no externals polluting the global namespace. If I want to render at least one of these components, I need to include the react-common chunk as well as the very small file(s) containing the component(s).

The important parts of the webpack 5 config are like this:

{
  entry: [
    'my-component-1': './react-dir/entries/component-1.jsx',
    'my-component-2': './react-dir/entries/component-2.jsx',
  ],
  // ... loaders, plugins
  optimization: {
    splitChunks: {
      cacheGroups: {
        reactDependencies: {
          test: /([\\/]node_modules[\\/](react[\\/]|react-(dom|toastify|transition-group))/, //defining all common react dependencies that should be split off because they are in more than one component
          name: "react-common",
          chunks: "all",
        }
      }
    }
  }  
}

I then define my component entries like so:

//react-dir/entries/component-1.jsx`
import React from 'react';
import ReactDOM from 'react-dom';
import Component1 from '../components/Component1';
const container = document.getElementById("idOfContainerWhereComponentShouldBeRenderedInto");

ReactDom.render(<Component1 />, container);