It used to be very difficult. These days, Webpack Module Federation makes it a breathe.
They have tons of samples to get you started. For example, the basic-host-remote sample has a simple scenario that fits your need. Just fetch the entire Github repo, cd
into this sample, and run it (as explained in each sample's README
).
Keep an eye open for the Network tab. Components are not loaded until they are explicitely requested.
Basic Example
The basic-host-remote sample has two separate packages: app1
and app2
.
app1
wants to use components from app2
app2
provides a Button
component
- app1/src/App.js loads the button dynamically. (It does so during initial render, making the on-demand loading a lot less obvious.)
- When the time comes,
app1
requests a component using dynamic import
.
- It wraps the load using React.lazy, like so:
const RemoteButton = React.lazy(() => import("app2/Button"));
- E.g., you can do this in a
useEffect
, or a Route.render
callback etc.
app1
can use that Button
, once it's loaded. While loading, it shows a loading message, using Suspense
:
<React.Suspense fallback={<LoadingScreen />}>
<RemoteButton />
</React.Suspense>
- Alternatively, instead of using
lazy
and Suspense
, you can just take the promise returned from the import(...)
statement and handle the asynchronous loading any way you prefer. Of course, WMF
is not at all restricted to react
and can load any module dynamically.
- Note that both,
app1
and app2
have the same shared
setup, making sure that those shared dependencies are only loaded one time, and not bundled/duplicated with remotely loaded code:
{
// ...
shared: { react: { singleton: true }, "react-dom": { singleton: true } },
}
Note that WMF
dynamic loading must use dynamic import
(i.e. import(...)
), because:
- non-dynamic imports will always resolve at load time (thus making it a non-dynamic dependency), and
- "dynamic
require
" cannot be bundled by webpack since browsers have no concept of commonjs
(unless you use some hacks, in which case, you will lose the relevant "loading promise
").
WMF Samples
WMF takes a bit of learning, but all its samples have the following common elements:
- Every sample has multiple independent packages.
- Each package has its own
webpack.config.js
- Each package usually acts as a host (which provides components dynamically) or a consumer of such components, or both.
- In
development
mode, you usually serve up everything using the nicely integrated webpack-dev-server
.
- NOTE: In
production
mode, you want to make some small adjustments to your config. When webpack-dev-server
is out of the picture, your build just adds some additional remoteEntry.js
files to your build output.
- You can easily configure what to do with common dependencies, using the
shared
configuration. (Usually you want them to act as singletons, meaning, everyone should share common dependencies, and not pack their own.)
- If you want to insist on keeping everything in one package, that is probably also possible. Just
expose
it and add its own (independent) components to its own remotes
. Not sure if it works, but its worth trying.
Final Words
Again, it will take a bit of a learning curve, but I find it's definitely worth it. Feels like one of the most sophisticated dynamic build + load systems I have seen.
Most annoyingly, WMF still does not currently have proper API documentation on the Webpack page, but I'm sure it'll come soon enough. Currently, there is only a not all too polished collection of conceptual notes.
The WMF author himself promises to provide better documentation soon™️.
Luckily, for learning, the samples are really good, actively maintained and still being enhanced.