some packages like react-i18next are not working properly with @module-federation/nextjs-mf in next js

I am folowing microforntends architecture using this module federation package "@module-federation/nextjs-mf".

I have 3 NextJS Apps

  1. One main-app which is the host application,
  2. Two microfrontends first-app and second-app here is the next.config.js for each app :

main-app : (HOST APPLICATION)

const path = require("path");
const { NextFederationPlugin } = require("@module-federation/nextjs-mf");
const { createDelegatedModule } = require('@module-federation/nextjs-mf/utilities');

const nextConfig = async () => {
  return {
  reactStrictMode: true,
  transpilePackages: ["ui", "core"],
  output: "standalone",
  experimental: {
    outputFileTracingRoot: path.join(__dirname, "../../"),
  },
  swcMinify: true,
  webpack: (config, options) => {
    const { isServer } = options;
    config.plugins.push(
      new NextFederationPlugin({
        name: "application",
        remotes: {
          firstapplication:"firstapplication@http://localhost:4001/_next/static/chunks/remoteEntry.js",
          secondapplication:"secondapplication@http://localhost:4002/_next/static/chunks/remoteEntry.js",
        },
       
        exposes: {
          "./Page": "./pages",
        },
        filename: "static/chunks/remoteEntry.js",
        extraOptions: {
          exposePages: true,
        },
      })
    );
    return config;
  },};
};

module.exports = nextConfig;

first-app: (MICROFRONTENDS)

const path = require("path");
const { NextFederationPlugin } = require("@module-federation/nextjs-mf");
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  transpilePackages: ["ui", "core"],
  output: "standalone",
  experimental: {
    outputFileTracingRoot: path.join(__dirname, "../../"),
  },
  swcMinify: true,
  webpack: (config, options) => {
    const { isServer } = options;
    config.plugins.push(
      new NextFederationPlugin({
        name: "firstapplication",
        remotes: {
          application: `application@http://localhost:4000/_next/static/${
            isServer ? "ssr" : "chunks"
          }/remoteEntry.js`,
        },
        exposes: {
          "./Page": "./pages",
        },
        filename: "static/chunks/remoteEntry.js",
        extraOptions: {
          exposePages: true,
        },
      })
    );
    return config;
  },
};

module.exports = nextConfig;

second-app: (MICROFRONTENDS)

const path = require("path");
const { NextFederationPlugin } = require("@module-federation/nextjs-mf");
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  transpilePackages: ["ui", "core"],
  output: "standalone",
  experimental: {
    outputFileTracingRoot: path.join(__dirname, "../../"),
  },
  swcMinify: true,
  webpack: (config, options) => {
    const { isServer } = options;
    config.plugins.push(
      new NextFederationPlugin({
        name: "secondapplication",
        remotes: {
          application: `application@http://localhost:4000/_next/static/${
            isServer ? "ssr" : "chunks"
          }/remoteEntry.js`,
        },
        exposes: {
          "./Page": "./pages",
        },
        filename: "static/chunks/remoteEntry.js",
        extraOptions: {
          exposePages: true,
        },
      })
    );
    return config;
  },
};

module.exports = nextConfig;

The main-app is showing those packages normaly this is the index of the main-app :

const FirstApp = dynamic(() => import("firstapplication/Page"), { ssr: false });
const SecondApp = dynamic(() => import("secondapplication/Page"), {
  ssr: false,
});
//  const App = lazy(() => window.next2.get('./firstapplication/Page').then((factory) => {
//   return {default: factory()}
// }));

//const App = lazy(() => import("firstapplication/Page"));
export default function Home() {
  const getUserByIdUseCase = useInjection(TYPES.IGetUserByIdUseCase);
  const getLoginUseCase = useInjection(TYPES.ILoginUseCase);
  const {i18n , t} = useTranslation();
 // const { featuresflags } = useFeaturesSync();

 
  const dispatch = useDispatch();
  const isAuthenticated = useSelector((state) => state.auth.isAuthenticated);
  const errorselector = useSelector((state) => state.error.error);
 

  //console.log("this is it +++ :" , flags) ;
  //console.log("this is it +++ :" , featuresflags) ;
  const auth = useSelector((state) => state.auth.auth);
  const [shouldRerender, setShouldRerender] = useState(true);



  // useEffect(() => {
  //   console.log("this is it useEffect :" , flags) ;
  //   console.log("this is it useEffect :" , featuresflags) ;
  //   if (JSON.stringify(flags) !== JSON.stringify(featuresflags)) {
  
  //     dispatch(featuresActions.setFeatures(flags));
  //   }
  // }, [flags, featuresflags, dispatch]);

  
 useEffect(()=>{
  //  const fetchData = async () => {
  //    const userId = 'someUserId';
  //    const response = await getUserByIdUseCase.execute(userId);
  //    alert('this is youre response'+JSON.stringify(response));
  //    console.log(response);
  //  };
  //  fetchData();

  

 },[]);

  const loginHandler = () => {
    const fetchData = async () => {
      const request = {
        username: "admin",
        password: "Mazars@@**",
      };
      const response = await getLoginUseCase.execute(request);
      const error = new CustomError("This is a custom error message");
      dispatch(errorActions.setError(error));
      //dispatch(featuresActions.setFeatures({amine:"amine"}));
      const AnotherError = new Error("This is an error message");
      dispatch(errorActions.setError(AnotherError));
    };
    fetchData();
  };
  const logoutHandler = () => {
    dispatch(authActions.logout());
  };
  const handleSelectionChange = (selectedValue) => {
    if (selectedValue === "first") {
      setShouldRerender(true);
      i18n.changeLanguage('fr');
      localStorage.setItem("language" , "fr");

    } else {
      setShouldRerender(false);
      i18n.changeLanguage('en');
      localStorage.setItem("language" , "en");
    }

    // You can perform any actions with the selected value here
  };

  return (
    <Box height="100vh" display="flex" flexDirection="column">
      <Box>
        <AppBar position="static">
          <Toolbar>
            <IconButton
              size="large"
              edge="start"
              color="inherit"
              aria-label="menu"
              sx={{ mr: 2 }}
            >
              <MenuIcon />
            </IconButton>

            {isAuthenticated && (
              <Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
                 {t('greeting')} { auth.username}
              </Typography>
            )}

            {errorselector !== null && <Typography variant="h6" component="div" sx={{ flexGrow: 1 }} > "here i am " {JSON.stringify(errorselector)}</Typography>}
            {!isAuthenticated && (
              <Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
               {t('greeting')} The Host Application
              </Typography>
            )}
            <SelectorComponent onSelectionChange={handleSelectionChange} />
            {isAuthenticated && (
              <Button color="inherit" onClick={logoutHandler}>
                Logout
              </Button>
            )}
            {!isAuthenticated && (
              <Button color="inherit" onClick={loginHandler}>
                Login
              </Button>
            )}
          </Toolbar>
        </AppBar>
      </Box>
      <Box flex={1} overflow="auto">
        {shouldRerender && (
          <FirstApp style={{ flex: 1, height: "100%" }}></FirstApp>
        )}
        {!shouldRerender && (
          <SecondApp style={{ flex: 1, height: "100%" }}></SecondApp>
        )}
      </Box>
      <Footer />
    </Box>
  );
}

The problem happens with 2 packages till now , this time with translation package i setup the translation package in the first-app and it works only if i use the microservice url when i access the microfrontend directly, but not when it’s integreated into the main-app (using the host application url ) , in first-app it render the correct translation but in the host application it render greeting and not the hello coming from the translation hook ('greeting')

Leave a Comment