feat(bridge): add application-level SSR for React and Vue#4869
feat(bridge): add application-level SSR for React and Vue#4869tonoizer wants to merge 7 commits into
Conversation
🦋 Changeset detectedLatest commit: 8813e97 The changes in this PR will be included in the next version bump. This PR includes changesets to release 52 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
✅ Deploy Preview for module-federation-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
2heal1
left a comment
There was a problem hiding this comment.
Leaving a few inline comments on the issues we discussed.
|
Pushed ed191ac — a small hardening pass from a full self-review of the branch:
Two API-level points worth a maintainer opinion (no code change here):
All 13 Playwright e2e specs and unit suites pass locally on this head. |
|
Pushed a hardening commit (37d9162) addressing three of the open discussion points from the description:
New unit tests in bridge-shared/bridge-react cover the fallback modes, timeout, DOM recovery, and ref-counting; a new |
|
Pushed f39a7d9 — architecture reset for the SSR foundation. Review identified that module-scoped hydration state is only correct for one remote mount. Rather than extend the reference-counted cache, this update replaces it with instance-scoped Bridge slots, explicit request/props input, and remote-owned dehydration. Each rendered remote now hydrates from its own container and state payload. The branch no longer rewrites React Router imports or fabricates data-router loader state. V1 supports explicit classic-router SSR through string adapters, while the shared result contract is stream-capable for a future non-breaking streaming adapter. First-class React Router data loaders remain out of scope for this PR. This also removes the unrelated Metro/schema drift and restores the valid |
d927242 to
c203d59
Compare
2915de3 to
2c3f1e5
Compare
Key the hydration registry by moduleName+instanceId, hydrate empty islands from markers, destroy React roots when mount DOM identity changes, require a registry for client references, and soft-cancel aborted Vue mounts so failed renders cannot leak remotes.
Claim registry snapshots before provider.render and fail them on cancel so SPA revisits cannot hydrate stale request-time state. KeepAlive reactivation falls back to CSR, and demo hosts stop re-seeding SSR refs after navigation. Co-authored-by: Cursor <cursoragent@cursor.com>
Description
Adds a deliberately narrow Bridge SSR V1 for React and Vue and hardens its hydration and lifecycle boundaries. A federated provider renders one buffered HTML island on the server; the host emits that HTML and its optional dehydrated state exactly once in an instance-scoped document slot, while serialized host context carries only a versioned module/instance reference.
Before React
hydrateRootor Vue application mount, the host installs a document hydration registry. The client resolves a reference to an immutable slot snapshot, validates its identity and direct-child structure, and consumes it once. Missing slots intentionally follow the existing CSR path; malformed, duplicated, or identity-conflicting slots fail with contextual hydration errors. After a successful consume, later SPA visits render through CSR rather than rehydrating stale server markup.The React adapter now keeps Hook structure and callback/ref handling stable, serializes asynchronous renders, rejects late mounts, forwards asynchronous failures to the host boundary, and destroys nested providers exactly once with deferred React-root cleanup. The Vue adapter retains one app/router across ordinary prop changes using shallow-reactive props, remounts only for mount-critical router changes, waits for router readiness during hydration, and prevents cancelled mounts from being published.
The public SSR result remains limited to protocol version, module name, instance ID, HTML, and optional JSON state. A separate identity-only reference and result-to-reference boundary prevent HTML from entering host JSON. React Router aliasing remains the V1 integration model, enforced by server/browser build and bundle-exclusion checks.
The Rsbuild demo covers React host to Vue remote and Vue host to React remote in development and production. A separate non-blocking Vite smoke path covers React host to Vue remote. Tests cover the registry contract, HTML-safe serialization, reference conversion, React/Vue lifecycle behavior, no-JS output, hydration, deep links with query/hash, interactivity, multiple instances, and SPA revisit behavior.
Bridge SSR V1 intentionally does not provide a shared timeout or CSR fallback for remote load/render failures. A slow or broken remote can therefore fail the host response. Assets, status/header policy, streaming, React data routers, Nuxt, and Modern.js adapters remain out of scope.
Validation
pnpm install --frozen-lockfilepnpm exec prettier --check .pnpm run build:packages(43/43 tasks)pnpm exec turbo run test --filter=@module-federation/bridge-shared --filter=@module-federation/bridge-react --filter=@module-federation/bridge-vue3(10/10 tasks; 88 focused tests)pnpm exec turbo run lint --filter=@module-federation/bridge-shared --filter=@module-federation/bridge-react --filter=@module-federation/bridge-vue3(successful no-op because these packages expose no Turbo lint tasks)pnpm run ci:local --only=e2e-router(26/26 tests)pnpm run ci:local --only=e2e-bridge-ssr(required development/production paths and browser-bundle gate passed; non-blocking Vite smoke passed)Local validation used the repository-pinned pnpm 10.28.0.
corepack enablecould not replace the system pnpm shim because/usr/local/binis not writable; no dependency or validation command relied on that replacement.Related Issue
Closes #4868
Types of changes
Checklist