vbe
vbe

Reputation: 133

Load React JS component from external script in "run time"

I'm using React JS + webpack. General case that I need to resolve is to dynamically load React components that were not bundled with main application. Kind of pluggable components that can be developed independently from main app and then loaded by application dynamically, without rebuilding whole app.

Particular case is below.

I have two completely separated modules (i.e. built using different package.json and webpack.config.js):

I need to implement following behaviour:

  1. Page with MainApp loaded and initialized.
  2. MainApp dynamicaly lookup for url of .js file that contains Component (e.g. by making GET request to web-server).
  3. MainApp loads .js file with Component and include it to page as <script>
  4. MainApp uses loaded Component while rendering.

Is such use-case possible in react js + webpack?

Upvotes: 8

Views: 5335

Answers (2)

flexicious.com
flexicious.com

Reputation: 1671

It sounds like you are asking how to externalize React. If so, you can list libraries as "externals" in your webpack.config.js file:

webpackConfig.externals = {
  "react": "React",
  "react-dom": "ReactDOM",
  ...
}

Upvotes: -1

NSjonas
NSjonas

Reputation: 12081

With webpack 5 you can now do this via module federation.

The basic idea is that you "expose" exports from one project to be used in another:

App 1 (uses Button from app2)

<!-- public/index.html -->
<html>

<head>
  <!-- remote reference to app 2 -->
  <script src="http://localhost:3002/remoteEntry.js"></script>
</head>

<body>
  <div id="root"></div>
</body>

</html>

//app.ts
import * as React from "react";

const RemoteButton = React.lazy(() => import("app2/Button"));

const App = () => (
  <div>
    <h1>Typescript</h1>
    <h2>App 1</h2>
    <React.Suspense fallback="Loading Button">
      <RemoteButton />
    </React.Suspense>
  </div>
);

export default App;

//... webpack.config.js
plugins: [
    new ModuleFederationPlugin({
      name: "app1",
      library: { type: "var", name: "app1" },
      remotes: {
        app2: "app2",
      },
      shared: ["react", "react-dom"],
    }),
    new HtmlWebpackPlugin({
      template: "./public/index.html",
    }),
  ]

App 2 (exposes Button)

// src/Button.ts
import * as React from "react";

const Button = () => <button>App 2 Button</button>;

export default Button;

//... webpack.config.js
 plugins: [
    new ModuleFederationPlugin({
      name: "app2",
      library: { type: "var", name: "app2" },
      filename: "remoteEntry.js",
      exposes: {
        Button: "./src/Button",
      },
      shared: ["react", "react-dom"],
    })
  ]

Here is a repo containing various examples.

Upvotes: 2

Related Questions