Skip to content

Problem with Dynamic Remote in Module Federation Setup [Webpack] [React] [MicroFrontends] #30408

@Przemyslaw-Swiatlon

Description

@Przemyslaw-Swiatlon

Current Behavior

  • Remotes are not dynamically loaded from env as expected when using nx serve host.
  • The configuration is overridden and does not respect the environment variables for dynamic URLs.
  • React hooks such as useState and useMemo are not working, causing errors, when using:
    "executor": "@nx/react:module-federation-dev-server" (nx serve host)
  • There's a problem in checking version of package eg: react-dropzone, resulting in errors despite having the correct version in package.json, when using: "executor": "@nx/webpack:dev-server" (nx run many)

Expected Behavior

  • Dynamic URLs for remotes should be respected and properly loaded when running the host application.
  • The @nx/react:module-federation-dev-server should work without causing issues related to React hooks (or other packages).
  • Correct versions of dependencies should be loaded across remotes, ensuring compatibility. (only when: @nx/webpack:dev-server)
  • The preferred way of loading remotes dynamically should involve fetching remoteEntry from environment variables when clicking on a link in the host.

GitHub Repo

No response

Steps to Reproduce

I don't described here the nx serve host because it’s not working at all. Using Nx run many with remotes: [] is a closer working example.

  1. Set up a host application using @module-federation/enhanced/runtime for dynamic remotes.
  2. Define remotes in the host application’s main.ts using environment variables for the URLs (as shown in the provided code below).
  3. Use the command nx run-many --parallel --target=serve --projects=host,remoteOne,remoteTwo.
  4. Observe that the remotes are not correctly loaded based on the dynamic environment variables. (eg: fetched port from project.json, not from env)
  5. Replace the executor in project.json with @nx/webpack:dev-server and got mismatch error for react-dropzone

Nx Report

Node           : 18.20.4
OS             : darwin-x64
Native Target  : x86_64-macos
npm            : 10.7.0

nx (global)            : 20.5.0
nx                     : 20.5.0
@nx/js                 : 20.5.0
@nx/jest               : 20.5.0
@nx/eslint             : 20.5.0
@nx/workspace          : 20.5.0
@nx/cypress            : 20.5.0
@nx/devkit             : 20.5.0
@nx/eslint-plugin      : 20.5.0
@nx/module-federation  : 20.5.0
@nx/react              : 20.5.0
@nx/rollup             : 20.5.0
@nx/web                : 20.5.0
@nx/webpack            : 20.5.0
typescript             : 5.7.3

Failure Logs

When using @nx/react:module-federation-dev-server:

hook.js:608 Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this.

When using @nx/webpack:dev-server:
Error: [ Federation Runtime ]: Version 0 from remoteTwo of shared singleton module react-dropzone does not satisfy the requirement of remoteTwo which needs ^14.3.8)

Package Manager Version

No response

Operating System

  • macOS
  • Linux
  • Windows
  • Other (Please specify)

Additional Information

Code:

Environment Configuration:

const {
  NX_REMOTE_ONE_URL,
  NX_REMOTE_TWO_URL
} = window._env_;

export const environment = {
  remoteOne: NX_REMOTE_ONE_URL,
  remoteTwo: NX_REMOTE_TWO_URL,
};

Host Application (main.ts):

import { init } from '@module-federation/enhanced/runtime';
import { environment } from './environments/environment';

init({
  name: 'host',
  remotes: [
    {
      name: 'remoteOne',
      entry: `${environment.remoteOne}/remoteEntry.js?v=${+Date.now()}`,
    },
    {
      name: 'remoteTwo',
      entry: `${environment.remoteTwo}/remoteEntry.js?v=${+Date.now()}`,
    },
  ],
});

import('./bootstrap').catch((err) => console.error(err));`

App Component (app.tsx):

const remoteOne = React.lazy(() =>
  loadRemote('remoteOne/Module').then((module: any) => {
    return { default: module.default };
  })
);

const remoteTwo = React.lazy(() =>
  loadRemote('remoteTwo/Module').then((module: any) => {
    return { default: module.default };
  })
);

Module Federation Config (module-federation.config.ts) for Host:

import { ModuleFederationConfig } from '@nx/module-federation';

const coreLibraries = new Set([
  'react',
  'react-dom',
  'react-router-dom',
  'react-redux',
  'react-i18next',
  'i18next',
  'react-dropzone',
  'styled-components',
]);

const config: ModuleFederationConfig = {
  name: 'host',
  remotes: [],

  shared: (libraryName, defaultConfig) => {
    if (coreLibraries.has(libraryName)) {
      return {
        ...defaultConfig,
        eager: true,
        singleton: false,
      };
    }
    return false;
  },
};

export default config;

Module Federation Config (module-federation.config.ts) for Remote:

import { ModuleFederationConfig } from '@nx/module-federation';

const config: ModuleFederationConfig = {
  name: 'remoteOne',

  exposes: {
    './Module': './src/remote-entry.ts',
  },
};

export default config;

Helpers Questions:

  1. Do serve need to build all remotes when we need only host + one remote ?
  2. Is it possible that nx module federation ovverwrite @enchanted (when running with serve) ?
  3. What's the consequences of changing executors ?
  4. What's the proper way to setup app like this ? In Nx 16 it was possible with custom script that was setting and loading remotes. Similiar to this one: https://medium.com/@wangel13/dynamic-micro-frontends-with-nx-and-react-d04a350732a4

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions