LEARN · DEBUGGING GUIDE

Webpack Module Federation Remote Not Loading: Debugging Guide

A remote module that silently fails to load is almost always a shared dependency mismatch, a missing exposes entry, or a CORS issue. This guide shows you exactly how to find which one.

AdvancedBuild tools7 min read

What this usually means

Webpack Module Federation relies on a specific contract between host and remote: the remote exposes a `remoteEntry.js` that registers its modules and shared dependencies. If that contract breaks — due to version mismatch of shared libraries, incorrect `exposes` config, CORS blocking, or even an async loading race — the host cannot resolve the remote's exports. The failure often surfaces as a silent undefined or a generic loading error that points to the federation internals rather than your actual code.

( 01 )Fast diagnosis

The first ten minutes — establish facts before touching code.

  • 1Open browser DevTools Network tab, filter by 'remoteEntry', and reload. Check if the remoteEntry.js returns 200 with JavaScript content (not HTML).
  • 2In the host's console, run `__webpack_require__.federation.remotes` — it should show the remote's scope and module map. If empty, the host didn't parse the container.
  • 3Check the remote's webpack config: confirm `exposes` includes the component path (e.g., `'./MyComponent': './src/MyComponent'`). A missing expose returns undefined.
  • 4Verify shared dependencies: both host and remote must declare the same version of shared libraries (e.g., React). Use `shared: { react: { singleton: true, requiredVersion: '^17.0.0' } }`.
  • 5Inspect the remoteEntry.js file: search for `__webpack_require__.federation` and confirm it contains the expected modules. If not, the build is corrupted.
  • 6Check CORS headers: if remote is on a different origin, ensure the server sends `Access-Control-Allow-Origin: *` or the host's origin. A missing header causes silent failure.
( 02 )Where to look

The specific files, logs, configs, and dashboards that usually own this bug.

  • searchHost's webpack.config.js: module federation plugin `remotes` entry — check URL syntax (trailing slash, protocol).
  • searchRemote's webpack.config.js: `exposes` array — ensure paths are correct and components are exported as default.
  • searchNetwork tab: remoteEntry.js response body and HTTP status code. If it's 304, the content might be cached stale.
  • searchBrowser console: look for `ScriptExternalLoadError` or `ChunkLoadError` with the remote URL.
  • searchShared dependency versions: mismatch in `package.json` or webpack `shared` config (e.g., React 17 vs 18).
  • searchServer configuration: CORS headers, CSP directives (style-src, script-src).
( 03 )Common root causes

Practical causes, not theory. These are the things you will actually find.

  • warningShared dependency version mismatch — e.g., host uses React 18, remote uses React 17, and one is not a singleton.
  • warningMissing or incorrect `exposes` entry — the remote doesn't export the requested module.
  • warningCORS policy blocking the remoteEntry.js request — especially in development with different ports.
  • warningAsync loading race — host initializes before remote is fully loaded (fix: use `promise` in remotes or loadRemoteEntry early).
  • warningWebpack cache or stale build — remoteEntry.js is from an old build without the updated exposes.
  • warningIncorrect remote URL — extra slash, wrong port, or protocol mismatch (HTTP vs HTTPS).
  • warningShared dependency not marked as singleton — causes multiple instances of React, breaking hooks.
( 04 )Fix patterns

Concrete fix directions. Pick the one that matches your root cause.

  • buildAlign shared dependency versions: use `requiredVersion: false` or `requiredVersion: '^version'` and `singleton: true` to allow single instance.
  • buildEnsure exposes paths are correct: use absolute paths or relative to project root, and verify the component file exports a valid default.
  • buildAdd CORS headers: in development, use `devServer.headers: { 'Access-Control-Allow-Origin': '*' }` on the remote.
  • buildUse promises in remotes config to delay host initialization: `remotes: { app2: 'promise new Promise(...)' }` or use `loadRemoteEntry`.
  • buildClear webpack cache: delete `node_modules/.cache` and rebuild both host and remote.
  • buildCheck remote URL in production: ensure the deployed remoteEntry.js path matches the remotes config (no trailing slash).
( 05 )How to verify

A fix you cannot prove is a guess. Close the loop.

  • verifiedOpen the remoteEntry.js URL directly in a browser — it should download a valid JS file, not an HTML page.
  • verifiedIn console, run `__webpack_require__.federation.remotes` — host should have a map with the remote's scope.
  • verifiedAdd a console.log inside the remote component's default export and confirm it appears in the host.
  • verifiedUse `window.__FEDERATION__` (if using @module-federation/enhanced) to inspect the container.
  • verifiedMonitor network tab for successful chunk loading after the remoteEntry.js request.
  • verifiedRun `npx webpack --config webpack.config.js --json` on remote and check `exposes` in the stats output.
( 06 )Mistakes to avoid

Things that make this bug worse or harder to find.

  • warningDon't assume remote works because it loads independently — federation requires specific exposes and shared config.
  • warningAvoid using `singleton: false` for critical shared deps like React — it creates multiple instances and breaks hooks.
  • warningDon't ignore CORS errors — even if the request succeeds, missing headers can cause silent failures.
  • warningAvoid stale builds — always rebuild both host and remote after changing federation config.
  • warningDon't use relative paths in remotes URL — use absolute URLs (e.g., http://localhost:3001/remoteEntry.js).
  • warningAvoid complex version ranges in requiredVersion — use `false` or a specific version to simplify debugging.
( 07 )War story

Remote module fails to load after upgrading React from 17 to 18

Senior Frontend EngineerWebpack 5.74, React 18, TypeScript, React Router 6

Timeline

  1. 09:15Deploy remote app (shell) with React 18 upgrade to staging.
  2. 09:20Host app (also React 18) shows blank page for remote component. Console: 'Cannot read properties of undefined (reading 'get')'.
  3. 09:22Check remoteEntry.js — returns 200 with valid JS. Network tab shows all chunks loaded successfully.
  4. 09:25Inspect __webpack_require__.federation.remotes — scope exists but module map is empty.
  5. 09:30Compare remote webpack config: shared: { react: { singleton: true, requiredVersion: '^17.0.0' } } — still pinned to 17!
  6. 09:35Update requiredVersion to '^18.0.0' in both host and remote. Rebuild and deploy.
  7. 09:40Remote component renders correctly. Console shows no errors.
  8. 09:45Run integration tests — all pass. Incident resolved.

We had just upgraded our entire frontend ecosystem from React 17 to 18. The host app went smoothly, but when we deployed the remote (the shell that provides the header and footer), the header component simply didn't appear. Console showed 'Cannot read properties of undefined (reading 'get')' — a classic federation error meaning the remote module wasn't being resolved.

The remoteEntry.js was being served correctly — I checked the network tab and it returned 200 with a large JavaScript payload. But when I ran `__webpack_require__.federation.remotes` in the console, the module map was empty. That told me the remote's container didn't register any modules, likely because shared dependency negotiation failed.

I opened the remote's webpack config and there it was: `shared: { react: { singleton: true, requiredVersion: '^17.0.0' } }`. We had updated the package.json but forgot to update the webpack config. The host, running React 18, tried to share its React 18, but the remote demanded ^17.0.0. The negotiation failed, and the remote fell back to loading its own React 17 — but then the exposes couldn't initialize because the host's React 18 was already on the page. Fixed the version to '^18.0.0', rebuilt both apps, and the component appeared instantly.

Root cause

Webpack shared module version negotiation failed because the remote's webpack config still required React 17, while the host provided React 18.

The fix

Updated the `requiredVersion` for React in both host and remote webpack configs to `'^18.0.0'` and added `singleton: true`. Rebuilt and redeployed both apps.

The lesson

Always keep the `shared` versions in webpack config synchronized with your actual package versions. Use `requiredVersion: false` to bypass version checks if you're confident about singletons, but better to match explicitly.

( 08 )How Module Federation Resolves Remote Modules

When the host loads a remote component, it first fetches the remote's `remoteEntry.js`. This script registers the remote's container with `__webpack_require__.federation`. The container includes a module map of the exposed modules and a shared scope that lists the remote's shared dependencies with their versions.

The host then negotiates shared dependencies: it compares its own shared versions with the remote's. If a match is found (or fallback is allowed), the host uses its own version. If not, the remote may load its own version, but this can cause multiple instances of a library like React. The negotiation result determines whether the remote's modules can be instantiated. If negotiation fails silently, the module map remains empty, and the host gets undefined.

( 09 )Debugging Shared Dependency Negotiation

The most common cause of a remote not loading is shared dependency version mismatch. To debug, check the console for warnings like "Unsatisfied version" or "Version mismatch". You can also inspect `__webpack_require__.federation.initOptions` to see what shared scopes are registered.

A practical approach: set `shared: { react: { singleton: true, requiredVersion: false, eager: true } }` in both host and remote temporarily. This bypasses version checks and forces a single instance. If the remote loads, the issue is version negotiation. Then gradually reintroduce version constraints.

Use the `@module-federation/enhanced` plugin to get more verbose logging: set `debug: true` in your webpack config to see federation initialization logs.

( 10 )CORS and Deployment Pitfalls

In development, remotes often run on different ports (e.g., host on :3000, remote on :3001). Browsers block cross-origin requests unless CORS headers are present. The host fetches `remoteEntry.js` via script tag, which is not subject to CORS? Actually, dynamic imports (used by federation) are subject to CORS. You'll see a network error in the console if the remote server doesn't send `Access-Control-Allow-Origin`.

In production, ensure your CDN or server adds the proper CORS headers. Also check that the remote URL is accessible from the host's domain — no firewalls or authentication intercepting the request. A common mistake: the remote's `publicPath` is set to an absolute URL that doesn't match the deployed path, causing 404s for chunk files.

Frequently asked questions

Why does my remote component appear blank with no error?

This usually happens when the remote module resolves to an empty object or undefined. Check the remote's exposes: ensure the component is exported as a default export. Also verify that the shared dependency versions are compatible — a failed negotiation can cause the remote to fall back to its own version, but if that version conflicts with the host, the component may silently fail to render.

How do I force Webpack to use a single version of React across host and remote?

In both apps' webpack config, set `shared: { react: { singleton: true, requiredVersion: false } }`. This tells Webpack to share the first loaded instance of React and not enforce version checks. However, ensure both apps are using the same major version of React to avoid runtime errors.

Why does my remote work in development but not in production?

Common causes: (1) CORS headers not set on the production server, (2) remote URL is incorrect (e.g., missing trailing slash or wrong path), (3) webpack `publicPath` in the remote points to a localhost URL, (4) production builds may have code splitting that changes chunk filenames — ensure `output.uniqueName` is set to avoid conflicts.

What does 'ScriptExternalLoadError: Loading script from ... failed' mean?

This error indicates that the host tried to load the remote's `remoteEntry.js` or a chunk but the request failed. Check the network tab for the exact URL that failed. Possible reasons: the remote server is down, the URL is wrong, CORS blocks the request, or the script is blocked by CSP (Content Security Policy).

How do I inspect the remote container at runtime?

In the browser console, run `__webpack_require__.federation.remotes` to see the remote scopes. For more details, access the remote's container directly: `__webpack_require__.federation.remotes['app2'].get('./MyComponent')`. If you use @module-federation/enhanced, you can use `window.__FEDERATION__` to inspect containers.