Repository navigation
feat: scaffold TanStack Module Federation plugin - #1
Merged
Merged
Conversation
commit: |
- Make Rsbuild SSR federation opt-in and eager React host shares - Add browser tests for hydration, remote interaction, and offline fallbacks - Document support boundaries and update release workflows
The dev-only dynamic-remote-type-hints runtime plugin opens a WebSocket to the federated type server. On CI runners the socket races host startup and logs console errors, which fails the browser tests on every Node version. Type generation is unchanged.
- pnpm 12.9.1: move settings from .npmrc to pnpm-workspace.yaml and let pnpm/action-setup read the version from packageManager - TypeScript 7.0.2, @types/node 26, turbo 2.11, prettier 3.9.9, Rslib 1.0.3 - Module Federation 2.9.2 / @module-federation/vite 1.23.2, TanStack Start 1.168.60, Router 1.170.41, Vite 8.3.2, Rsbuild 2.2.11 - Bump actions/checkout to v7.0.1 and pnpm/action-setup to v6.1.0 - Build the CJS library before ESM: under TypeScript 7 the CJS declaration pass renames every .d.ts in dist, including the ESM declarations
With server: true, a remote builds its Node container in a dedicated environment, emitted to dist/client/ssr/ with only the exposed modules, and advertises it in the browser manifest as ssrRemoteEntry. A host loads remotes on the server from an async-node CommonJS build that keeps dist/server/index.js as its entry. - default experiments.asyncStartup and role-aware React sharing (eager for hosts, lazy for remotes and containers) - give remotes publicPath "auto": TanStack Start derived "/" from server.base, so production remotes loaded chunks from the host's origin - write host server output to disk in development, where async-node chunks are read from disk while the entry runs from memory - add @module-federation/tanstack/node-entry-loader, which loads CommonJS remote entries without import(), unavailable in Rsbuild's dev runner - add rsbuild-ssr-remote and rsbuild-ssr-host fixtures
The six apps now run twice, as development servers and through vite preview and rsbuild preview. Each run checks server-rendered remote markup for concurrent first requests, reachable manifest assets, hydration, remote interactivity, console errors, and fallback and recovery while remotes are offline. Host loaders ignore a failed remote import, so an offline remote renders the boundary fallback instead of failing the route. The development run does not stop the Vite remote: @module-federation/vite's dev SSR transport then exits the Vite host with an unhandled rejection.
Mark Rsbuild-to-Rsbuild SSR as supported, describe the server: true layout, list known upstream limitations (standalone Vite remotes, the dev SSR crash, remote CSS in SSR responses), narrow the documented Vite support to the tested Vite 8, and restructure TODO.md around what 0.1.0 proves.
Each host provides its name through a React context from the shared example-host-context package, and every remote card renders it. The browser and server scenarios assert the host's name in each card, including the Vite remote inside the Rsbuild host and server-rendered HTML. A Vite host renders before lazy shares resolve, so the Vite host provides the package eagerly; the README now states that rule for Vite hosts.
Checked by hand with Vite 7.3.7 and @vitejs/plugin-react 5.2: browser federation works, but production SSR hosts fail to load remotes because the server bundle keeps a virtual: import that Node rejects.
Every Vite and Rsbuild host and remote pair now renders on the server, in development and production: - Rsbuild hosts hand Vite remotes' ES module entries to @module-federation/vite's SSR loader, loaded with Node's require so it also works in Rsbuild's dev runner. The host's shared modules are registered under their node_modules paths, so a Vite dev server's externalized React is the host's React. - Vite hosts load Rsbuild server containers, which move to remoteEntry.ssr.cjs next to the manifest (chunks stay in ssr/). In development, saved remote entries are imported through Node instead of Vite's module runner, which evaluated CommonJS as ES modules. Hosts now render a remote again after a failed first load, such as a host started during an outage. Four caches kept the failure: the Vite server wrapper's single load attempt, Vite's dev module runner caching the wrapper's rejected then export, Rspack's module cache keeping a failed remote module's empty exports, and React.lazy. The adapters patch the first three; @module-federation/tanstack/runtime adds lazyRemote for the last. The runtime subpath also exports getRemoteStylesheets, which reads an expose's CSS from the remote manifest for the route head, so server-rendered remote markup is styled before JavaScript loads. The node entry loader throws RemoteEntryError with the failing phase and the original cause, and bounds its fetches. The Vite adapter requires Vite 8 and stops with an error on older versions, and handles the unawaited __mf_remote_pending export that exited dev servers when a remote went offline. The example scenarios cover cross-bundler SSR, styling with JavaScript disabled, and hosts started during an outage. The docs describe the server rendering pattern, the React 19 requirement, and the first npm publish.
This was referenced Oct 6, 2026
Merged
The Vite adapter's workarounds and TODO.md now point at module-federation/vite#1421-#1424, so each workaround can go once its fix ships.
…of dist @module-federation/vite 1.23.3 ships the fixes reported from this package (module-federation/vite#1421, #1422, #1425, #1426), so the Vite adapter drops its workarounds: the remote wrapper retry and the dev temp-file import. The Rsbuild server container no longer needs static named-export re-assignments. Every remote now also runs as a standalone app, so the example scenarios test the Vite remote standalone too, instead of marking it todo. A host's Rsbuild dev server wrote its server bundle to dist/server, replacing a production build that `rsbuild preview` serves. The dev bundle now goes to node_modules/.cache/tanstack-start-federation/, and the development scenarios check that dist/server is left alone.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Prepare this repository to publish
@module-federation/tanstackfor TanStack Start on Vite and Rsbuild/Rspack. The package now isolates each bundler adapter behind its own export and uses optional peer dependencies, so a project only installs and resolves the Module Federation plugin for its bundler.Rslib builds conditional ESM and CommonJS outputs with matching declarations. Four runnable TanStack Start applications exercise Vite, Rsbuild, same-bundler federation, and reciprocal client-side manifest interoperability.
Changes
/viteadapter using@module-federation/vite/rsbuildusing@module-federation/rsbuild-plugin, with browser script/JSONP compatibility and optional async-node CommonJS SSR output.js/.d.tsESM plus.cjs/.d.ctsCommonJS artifactsapps/vite-hostandapps/vite-remote, alongside the newRsbuild/Rspack host and remote apps
TODO.mdTesting
pnpm install --frozen-lockfilepnpm format:checkpnpm exec tsc -b --force --pretty falsepnpm typecheckpnpm test(22 tests, including live Vite/Rsbuild runtime shells)pnpm pack:tanstackactionlintdocs-listgit diff --checkReview notes
Accepted findings fixed during review: remote loader stale state, Vite hydration mismatch, and preserving ESM filenames when CommonJS forcing is disabled.
Two findings were rejected with direct build evidence: Rslib 1.0.1 accepts the
--lib esmand--lib cjsselectors used here, and Rsbuild's environment-scoped Module Federation instances produce both browser and SSR manifests despite sharing the upstream plugin name.Follow-up
Follow
TODO.mdfor the Rsbuild SSR fixture, cross-bundler server-entry transport,shared React identity, outage recovery, and version matrix. The current examples
and tests claim client manifest interoperability only, not cross-bundler SSR
remote rendering.