Skip to content

TanStack Start compatibility and support #498

Closed

Description

@alexandre-marchina

Describe the bug

Context

I'm integrating @module-federation/vite with TanStack Start. I've created a minimal reproduction repository demonstrating the issue:

Repository: https://github.com/alexandre-marchina/tanstack-start-with-module-federation

The Problem

When using Module Federation with TanStack Start, the application crashes during SSR:

  TypeError: Cannot destructure property 'Activity' of 'exportModule' as it is undefined.
      at eval (.vite/deps_ssr/host__loadShare__react__loadShare__*.js)

I noticed that PR #474 added SSR support for Vinext using framework detection. Perhaps some of the fixes that were added could be reused in other scenarios?

But I may be missing something here since I'm not very familiar with this topic

Reproduction

The repository contains two host configurations:

  • ts-host/ - MF config with hostInitInjectLocation: "entry" (crashes)
  • ts-host-with-CSR-remotes/ - With additional custom workarounds (works)

Steps:

  1. Start remotes (ts-remote-bff, ts-remote-simple)
  2. Start ts-host → crashes on first SSR request
  3. Compare with ts-host-with-CSR-remotes → works

Questions

  1. Is TanStack Start within scope for this plugin?

  2. Are there configuration options I might be missing that would address this?

    • I've reviewed hostInitInjectLocation, target, and shared options

Environment

  • @module-federation/vite: 1.13.0
  • vite: 8.0.0
  • @tanstack/react-start: 1.166.16
  • Node.js: 24.x

Version

1.13.0

Reproduction

https://github.com/alexandre-marchina/tanstack-start-with-module-federation

Relevant log output

TypeError: Cannot destructure property 'Activity' of 'exportModule' as it is undefined.
      at eval (.vite/deps_ssr/host__loadShare__react__loadShare__*.js)

Validations

Activity

  1. gioboa commented on Mar 18, 2026

    @gioboa
    Collaborator

    Thanks for your help, I can use the reproduction repo to support TanStack Start too. Btw here I have the pipeline to tests the updates. I created a TanStack example too.

  2. added
    enhancementNew feature or request
    COMMUNITY: PR is welcomedWe think it's a good feature to have but would love for the community to help with the PR for it
    and removed
    bugSomething isn't working
    on Mar 18, 2026
  3. gioboa commented on Mar 26, 2026

    @gioboa
    Collaborator

    Blocked by TanStack/router#7032
    So I'm closing this issue for now. Looking forward to seeing this working.

  4. removed
    enhancementNew feature or request
    COMMUNITY: PR is welcomedWe think it's a good feature to have but would love for the community to help with the PR for it
    on Mar 26, 2026
  5. gioboa commented on May 3, 2026

    @gioboa
    Collaborator

    Here is a basic example https://github.com/rr-jimmy-multani/rr-tanstack-poc
    You can use it to verify and improve the mfe/vite library.

  6. reopened this on May 3, 2026
  7. added
    enhancementNew feature or request
    help wantedExtra attention is needed
    COMMUNITY: PR is welcomedWe think it's a good feature to have but would love for the community to help with the PR for it
    on May 3, 2026
  8. gioboa commented on May 15, 2026

    @gioboa
    Collaborator

    Full SSR rendering support example

    Image
  9. jsg2021 commented on Jun 17, 2026

    @jsg2021
    Contributor

    @gioboa How did you get past tanstack start's compiler not able to access the host shared virtual modules?

    Image
  10. gioboa commented on Jun 17, 2026

    @gioboa
    Collaborator

    Hi @jsg2021
    What are the differences between your and the example above?

  11. jsg2021 commented on Jun 17, 2026

    @jsg2021
    Contributor

    @gioboa good question. I've basically grabbed the federation / nitro config sections from your example and added them to my app, minus remotes... I was just hoping to start with a booting app and work my way towards pulling in remotes and exposing components. But I think I need the compiler changes from TanStack/router#6783

  12. gioboa commented on Jun 18, 2026

    @gioboa
    Collaborator

    Can you share a basic example that shows the issue?

  13. jsg2021 commented on Jun 18, 2026

    @jsg2021
    Contributor

    In an attempt to create an example...
    I just tried to create a new project with tanstack's create command, then applied these changes (same changes from my internal app) and it works here :/ I'm going to dig further. I'm not sure whats different.

    diff --git a/dist/esm/start-compiler/compiler.js b/dist/esm/start-compiler/compiler.js
    index f1d85eb32a8bff6ba6bf36b45c6dcc895edb4ba8..48996ec301eb3900735d8b977d3b556a3ad27c4a 100644
    --- a/dist/esm/start-compiler/compiler.js
    +++ b/dist/esm/start-compiler/compiler.js
    @@ -956,7 +956,24 @@ var StartCompiler = class {
     		if (cached) return cached;
     		await this.options.loadModule(id);
     		cached = this.moduleCache.get(id);
    -		if (!cached) throw new Error(`could not load module info for ${id}`);
    +		if (!cached) {
    +			// The module could not be ingested into the compiler's cache. This is
    +			// expected for modules the compiler cannot introspect — virtual modules
    +			// owned by another plugin (e.g. Module Federation's
    +			// `virtual:mf:__mfe_internal__…__loadShare__…` share proxies), externals,
    +			// or assets. The kind-resolution crawl only follows import bindings to
    +			// detect server-fn/middleware/isomorphic-fn kinds; an opaque module
    +			// simply contributes none. Cache an empty record so we neither re-attempt
    +			// the load nor abort the whole transform with
    +			// "could not load module info for <id>".
    +			cached = {
    +				id,
    +				bindings: /* @__PURE__ */ new Map(),
    +				exports: /* @__PURE__ */ new Map(),
    +				reExportAllSources: []
    +			};
    +			this.moduleCache.set(id, cached);
    +		}
     		return cached;
     	}
     };
    diff --git a/dist/esm/vite/start-manifest-plugin/normalized-client-build.js b/dist/esm/vite/start-manifest-plugin/normalized-client-build.js
    index 70b83a2966803d6846c1264da3e1396dc07eeb27..85679de6cc6f6fd34751ffa994b4304e4de9ef9a 100644
    --- a/dist/esm/vite/start-manifest-plugin/normalized-client-build.js
    +++ b/dist/esm/vite/start-manifest-plugin/normalized-client-build.js
    @@ -31,7 +31,14 @@ function normalizeViteClientBuild(clientBundle) {
     	const cssContentByFileName = /* @__PURE__ */ new Map();
     	for (const chunk of chunksByFileName.values()) {
     		const bundleEntry = clientBundle[chunk.fileName];
    -		if (chunk.isEntry) {
    +		// Only the TanStack Start client entry is relevant here. Start configures
    +		// the client environment's Rollup input as `{ index: <client-entry> }`
    +		// (see planning.ts), so the app entry chunk always has `name === 'index'`.
    +		// Other plugins (e.g. Module Federation) inject their own entry chunks —
    +		// `remote-entry.js`, shared-module init chunks — which are also `isEntry`
    +		// but carry different names. Without this name guard the loop aborts with
    +		// "multiple entries detected". Ignore any entry that isn't the app entry.
    +		if (chunk.isEntry && bundleEntry.name === "index") {
     			if (entryChunkFileName) throw new Error(`multiple entries detected: ${entryChunkFileName} ${chunk.fileName}`);
     			entryChunkFileName = chunk.fileName;
     		}
    diff --git a/src/start-compiler/compiler.ts b/src/start-compiler/compiler.ts
    index d6b36fe6e87d2d1142c658f06b497a40271cc9e3..d1a6a472d65278d19a51f2c685955fe629eddc72 100644
    --- a/src/start-compiler/compiler.ts
    +++ b/src/start-compiler/compiler.ts
    @@ -1953,7 +1953,22 @@ export class StartCompiler {
     
         cached = this.moduleCache.get(id)
         if (!cached) {
    -      throw new Error(`could not load module info for ${id}`)
    +      // The module could not be ingested into the compiler's cache. This is
    +      // expected for modules the compiler cannot introspect — virtual modules
    +      // owned by another plugin (e.g. Module Federation's
    +      // `virtual:mf:__mfe_internal__…__loadShare__…` share proxies), externals,
    +      // or assets. The kind-resolution crawl only follows import bindings to
    +      // detect server-fn/middleware/isomorphic-fn kinds; an opaque module
    +      // simply contributes none. Cache an empty record so we neither re-attempt
    +      // the load nor abort the whole transform with
    +      // "could not load module info for <id>".
    +      cached = {
    +        id,
    +        bindings: new Map(),
    +        exports: new Map(),
    +        reExportAllSources: [],
    +      }
    +      this.moduleCache.set(id, cached)
         }
         return cached
       }
    diff --git a/src/vite/start-manifest-plugin/normalized-client-build.ts b/src/vite/start-manifest-plugin/normalized-client-build.ts
    index c14609bc6a44d59c05d17c0e10e5922f90db52fd..3c9c205510d60216715619f317a0fcc307e39bce 100644
    --- a/src/vite/start-manifest-plugin/normalized-client-build.ts
    +++ b/src/vite/start-manifest-plugin/normalized-client-build.ts
    @@ -48,7 +48,14 @@ export function normalizeViteClientBuild(
       for (const chunk of chunksByFileName.values()) {
         const bundleEntry = clientBundle[chunk.fileName] as Rollup.OutputChunk
     
    -    if (chunk.isEntry) {
    +    // Only the TanStack Start client entry is relevant here. Start configures
    +    // the client environment's Rollup input as `{ index: <client-entry> }`
    +    // (see planning.ts), so the app entry chunk always has `name === 'index'`.
    +    // Other plugins (e.g. Module Federation) inject their own entry chunks —
    +    // `remote-entry.js`, shared-module init chunks — which are also `isEntry`
    +    // but carry different names. Without this name guard the loop aborts with
    +    // "multiple entries detected". Ignore any entry that isn't the app entry.
    +    if (chunk.isEntry && bundleEntry.name === 'index') {
           if (entryChunkFileName) {
             throw new Error(
               `multiple entries detected: ${entryChunkFileName} ${chunk.fileName}`,
    
  14. jsg2021 commented on Jul 14, 2026

    @jsg2021
    Contributor

    Found why the test repo works and my main one does not. I have server functions that reference shared libraries.

    Snippet from claude:
    The server-fn:client plugin transforms any module whose source contains server-fn markers (createServerFn, .handler, .validator, …). To classify each call expression, StartCompiler crawls the import graph — resolveImportKind → resolveIdCached(source) → getModuleInfo — following import bindings module-to-module (compiler.js:869-893).

    When that crawl reaches one of your shared singletons (react, react-dom, @tanstack/react-router), the @module-federation/vite plugin has rewritten the import target into an internal virtual module — virtual:mf:__mfe_internal__hub__loadShare__react__loadShare__.js. The compiler's loadModule (in dev: environment.transformRequest(id + '?server-fn-module-lookup'); start-compiler-plugin/plugin.js:151) never populates the compiler's moduleCache for that MF virtual id, so getModuleInfo throws.

  15. gioboa commented on Jul 14, 2026

    @gioboa
    Collaborator

    Please open an issue for that, thanks

  16. jsg2021 commented on Jul 14, 2026

    @jsg2021
    Contributor

    while my instinct is for this to fix, the problem actually is a missing entry in tan-stack start's moduleCache which is not 'vite'.

    this is probably the proper solution:

    TanStack/router#6783

  17. gioboa commented on Jul 14, 2026

    @gioboa
    Collaborator

    Can you share with me a reproduction repo to show the problem?

  18. jsg2021 commented on Jul 14, 2026

    @jsg2021
    Contributor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    COMMUNITY: PR is welcomedWe think it's a good feature to have but would love for the community to help with the PR for itenhancementNew feature or requesthelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions