I am developing microfrontend using Webpack 5 Module Federation.
My host app is an Angular project. My remote app is an React. Both are using Typescript.
My setup is like following:
The React (remote) project
In React project (which is the remote app), I have App.tsx
under "src/".
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '../public/vite.svg'
import './App.css'
function App() {
const [count, setCount] = useState(0)
return (
<>
<div>
...
</div>
</>
)
}
export default App
The configuration inside webpack.config.js
is like this:
output: {
filename: "react-app.js",
publicPath: "http://localhost:4203/",
},
plugins: [
new ModuleFederationPlugin({
name: "reactApp",
filename: "remoteEntry.js",
exposes: {
// expose the App component
"./ReactApp": "./src/App",
},
shared: {
...deps,
react: { singleton: true, eager: true, requiredVersion: deps.react },
"react-dom": {
singleton: true,
eager: true,
requiredVersion: deps["react-dom"],
},
"react-router-dom": {
singleton: true,
eager: true,
requiredVersion: deps["react-router-dom"],
},
},
}),
new HtmlWebpackPlugin({
template: "./index.html",
}),
],
As you can see above, in module federation plugin configuration, I have:
exposes: {
// expose the App component
"./ReactApp": "./src/App",
},
to expose the App component in App.tsx
.
I successfully started this React app running on localhost:4203
The Angular (host) project
In the Angular project (which is the host app), the module federation plugin configuration in webpack.config.js
is like this:
plugins: [
new ModuleFederationPlugin({
library: { type: "module" },
remotes: {
// pointing to the remote React app
"reactApp": "http://localhost:4203/remoteEntry.js",
},
shared: share({
"@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
"@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
...sharedMappings.getDescriptors()
})
}),
sharedMappings.getPlugin()
],
As you can see, I have pointed to the running React project in the remotes:
configuration.
Under project src/
of this Angular project, I have also declared the React as moudle in a file called decl.d.ts
:
declare module 'reactApp/ReactApp';
In the routing rule, I have async loading code like this:
{
path: 'reactapp',
loadChildren: () => import('reactApp/ReactApp').then(m => m.App)
}
The above route is associated with the HTML element:
<a [routerLink]="['reactapp']">React App</a>
I can successfully start this Angular app too, I can also see that the remote React is reachable by this Angular app in developer tool (that http://localhost:4203/remoteEntry.js
is the React app):
But when I click on the HTML element link mentioned above, I get error:
Error: Uncaught (in promise): TypeError: fn is not a function
while loading "./ReactApp" from 2490
Why is that? What could be the reason?
OR if someone could guide me how to debug this would also be great! Thank you in advance!
=== Update: Please don't suggest me this ===
I know there are tutorials on internet says that on the host app's webpack configuration, I should point to the remote app by adding the name before the remote app URL like this:
remotes: {
"reactApp": "reactApp@http://localhost:4203/remoteEntry.js",
}
I tried it, the host app can not find my remote app when doing it, the error is:
Uncaught TypeError: Failed to resolve module specifier "reactApp@http://localhost:4203/remoteEntry.js". Relative references must start with either "/", "./", or "../".
That's why in my code snippet in my question, I point to the remote app in host's webpack configuration without the name prefix:
remotes: {
"reactApp": "http://localhost:4203/remoteEntry.js",
}
because it works, the host can find the remote in this way.