Thanks to visit codestin.com
Credit goes to github.com

Skip to content

fix: await host init in dev for bridge SSR hydration entries#835

Merged
gioboa merged 2 commits into
module-federation:mainfrom
tonoizer:fix/dev-hydration-host-init-race
Jun 17, 2026
Merged

fix: await host init in dev for bridge SSR hydration entries#835
gioboa merged 2 commits into
module-federation:mainfrom
tonoizer:fix/dev-hydration-host-init-race

Conversation

@tonoizer

Copy link
Copy Markdown
Contributor

Summary

Fixes a dev-only race when hostInitInjectLocation: "entry" is used with SSR hosts that have no root index.html (Nitro, TanStack Start, etc.).

Production builds already wrap hydration entries with getBootstrapSource() (await initHost() before the client entry runs). In Vite dev (serve), hydration entry fallbacks only received a side-effect import of the host-init module, so hydrateRoot could run before the Module Federation runtime finished initializing.

This shows up most clearly with @module-federation/bridge-react (createLazyComponent / loadRemote): bridge resolves the MF runtime synchronously on first render via getInstance(). If initHost() has not completed, remotes fail on first paint with: "Module Federation runtime is not initialized"

Production preview for the same apps works — the gap was dev-only client entry injection.

Affected setup

  • hostInitInjectLocation: "entry"
  • No index.html at dev time (or missing from project root)
  • Client entry contains hydrateRoot, createRoot, or ReactDOM.render
  • Vite command: "serve"

Typical frameworks: Nitro + React (entry-client.tsx), TanStack Start (client.tsx + StartClient).

React.lazy(() => import('remote/…')) with a route loader that pre-imports remotes may mask the same race; bridge should not require an app-level await hostInitPromise shim.

Root cause

In pluginAddEntry.ts, isHydrationEntryFallback matches client hydration modules when rollup has no registered input entry. For waitsForInit injections, usesDevEntryFallback forced dev serve to prepend only import hostInit instead of the full bootstrap wrapper used in build.

The old comment about SSR modules needing synchronous exports does not apply here — server entries do not match isHydrationEntryFallback (no mount/hydrate calls).

Nuxt dev is unchanged: entry.async.js is still skipped (skipNuxtDevEntryAsyncInject); host init is injected at mount time in entry.js.

@pkg-pr-new

pkg-pr-new Bot commented Jun 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@module-federation/vite@835

commit: 5cb33c6

@gioboa gioboa left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests With Different Frameworks

Screenshot 2026-06-17 at 15 44 50

@gioboa gioboa left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @tonoizer LGTM 💪

@gioboa gioboa merged commit b791a01 into module-federation:main Jun 17, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants