I’ve created simple react App HelloComponent and try to insert into Vue application but i’m unsuccessful
// React webpack config
const { ModuleFederationPlugin } = require('webpack').container;
const { merge } = require('webpack-merge');
const commonConfig = require('./webpack.common');
const path = require('path');
const devConfig = {
mode: "development",
entry: './src/index.jsx',
devServer: {
port: "5000",
},
plugins: [
new ModuleFederationPlugin({
name: 'HelloApp',
filename: "remoteEntry.js",
exposes: {
'./HelloComponent': './src/components/HelloComponent',
},
shared: { react: { singleton: true, eager: true }, "react-dom": { singleton: true, eager: true } },
})
],
}
module.exports = merge(commonConfig, devConfig);
// Vue webpack Config
const HtmlWebPackPlugin = require("html-webpack-plugin");
const { ModuleFederationPlugin } = require('webpack').container;
const { VueLoaderPlugin } = require("vue-loader");
const { TransformAsyncModulesPlugin } = require("transform-async-modules-webpack-plugin");
module.exports = {
entry: './src/bootstrap.js',
output: {
publicPath: "http://localhost:3002/",
},
resolve: {
extensions: [".jsx", ".js", ".json"],
},
devServer: {
port: 5001,
},
externals: {
react: 'react', // This tells Vue to use the React instance from the remote container
'react-dom': 'react-dom',
},
module: {
rules: [
{
test: /.m?js/,
type: "javascript/auto",
resolve: {
fullySpecified: false,
},
},
{
test: /.vue$/,
loader: "vue-loader",
},
{
test: /.css$/i,
use: ["style-loader", "css-loader"],
},
{
test: /.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ['@babel/preset-env'],
},
},
},
{
test: /.(jpe?g|png|gif|svg)$/i,
loader: 'file-loader',
options: {
name: 'src/assets/[name].[ext]'
}
}
],
},
plugins: [
new TransformAsyncModulesPlugin(),
new VueLoaderPlugin(),
new ModuleFederationPlugin({
name: "Vue App",
filename: "remoteEntry.js",
remotes: {
helloApp: 'HelloApp@http://localhost:3000/remoteEntry.js',
},
exposes: {},
shared: { react: { singleton: true, eager: true }, "react-dom": { singleton: true, eager: true } },
}),
new HtmlWebPackPlugin({
template: "./public/index.html",
}),
],
};
//App.vue
<template>
<HelloComponent :name="name" />
</template>
<script>
// Importing HelloComponent from remote React application
const HelloComponent = () => import("HelloApp/HelloComponent");
export default {
name: "App",
data() {
return {
name: "World",
};
},
components: {
HelloComponent,
},
};
</script>
My react App working as expected but Vue not able to render, Can someone help on this issue
I try for different way of insert into Vue but it’s not working. I’m little new in the Vue, Your response will be highly appriciated or Please suggest me any other approach, which will render react app in Vue
React version 18
and Vue version 3
Please suggest me any other solutions which is using webpack 5 and module federation
React
{
"name": "base-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"@babel/core": "^7.24.4",
"@babel/preset-env": "^7.24.4",
"@babel/preset-react": "^7.24.1",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.8",
"babel-loader": "^9.1.3",
"html-webpack-plugin": "^5.6.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"terser-webpack-plugin": "^5.3.10",
"web-vitals": "^2.1.4",
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.2",
"webpack-merge": "^5.10.0"
},
"scripts": {
"start": "webpack serve --config webpack.dev.js",
"build": "webpack build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Vue
{
"name": "vue-app",
"version": "0.1.0",
"private": true,
"scripts": {
"start": "webpack-dev-server --open --mode development",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.8.3",
"vue": "^3.2.13"
},
"devDependencies": {
"@babel/core": "^7.24.4",
"@babel/eslint-parser": "^7.12.16",
"@babel/plugin-transform-runtime": "^7.24.3",
"@babel/preset-env": "^7.24.4",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"@vue/compiler-sfc": "^3.4.25",
"babel-loader": "^9.1.3",
"css-loader": "^7.1.1",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.6.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"style-loader": "^4.0.0",
"transform-async-modules-webpack-plugin": "^1.1.0",
"vue-loader": "^17.4.2",
"vue-template-compiler": "^2.7.16",
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"not ie 11"
]
}
2
Answers
Webpack Module federation might also work, but there is a dedicated library that does exactly this.
Have a look here: https://github.com/devilwjp/veaury
You can use it with webpack,vite or vue cli
Hopefully, that does solve your problem.
I’ve created a monster for you.
Summary
Using React, I’ve crafted two example components named Counter and Input. I’ve also developed a function that creates a new Vue component and injects the provided React component into that Vue component.
It’s crucial that each initialization has a unique ID for the div where we insert the React component. Additionally, it’s important not to insert the React element into a div affected by reactive Vue parameters. This is why I had to insert a child div and reference it when invoking ReactDOM.
So, the essence of my example lies in the
getComponentFromReact()
function. And its usage is within the application’s components object.Example
Create React Component
So, it’s important to note that even though you import React components into Vue components, React elements can only be rendered by ReactDOM, as it possesses the necessary rendering logic. ReactDOM is capable of injecting the result into a native div, and by declaring this div as a Vue component, you can pass it to any Vue element as a "component".
Destroy React Component
Upon destruction of the Vue component, it’s essential to ensure that the React component is also properly notified of the event. It’s advisable to destroy the React component before our Vue component is destroyed. Therefore, within the
onBeforeUnmount()
hook, I invoke the destruction of the respective React component (which will be confirmed by a console.log message).Following this, the Vue component is automatically destroyed. I’ve illustrated this with an example using v-if, which can be managed by a boolean reactive variable.
If
v-if
is true, the Vue element mounts and creates the React element. Ifv-if
becomes false, not only does the component disappear, but it’s also entirely destroyed.