Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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):

  • The MainApp
  • Some Component

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?

like image 524
vbe Avatar asked Oct 29 '22 16:10

vbe


1 Answers

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.

like image 189
NSjonas Avatar answered Nov 15 '22 04:11

NSjonas