Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tidy-router-shares.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@module-federation/tanstack": patch
---

Share TanStack Router runtimes by default so router-aware remote components can preserve the host Router context across SSR bundlers.
1 change: 1 addition & 0 deletions apps/rsbuild-host/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dependencies": {
"@module-federation/runtime": "2.9.2",
"@tanstack/react-router": "1.170.41",
"@tanstack/router-core": "1.171.34",
"@tanstack/react-start": "1.168.60",
"example-host-context": "workspace:*",
"react": "19.3.0",
Expand Down
4 changes: 3 additions & 1 deletion apps/rsbuild-host/rsbuild.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ export default defineConfig({
dts: false,
name: "tanstack_rsbuild_host",
// Workspace dependencies declare "workspace:*", which is not a semver range.
shared: { "example-host-context": { requiredVersion: "*", singleton: true } },
shared: {
"example-host-context": { requiredVersion: "*", singleton: true },
},
// Resolve remotes on demand so one offline remote cannot block host startup.
shareStrategy: "loaded-first",
remotes: {
Expand Down
4 changes: 4 additions & 0 deletions apps/rsbuild-host/src/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ function Home() {
fallback="Loading the Vite remote…"
remote="tanstack_vite_remote/StatusCard"
/>
<RemoteCardSlot
fallback="Loading the Router-aware remote…"
remote="tanstack_vite_remote/RouterCard"
/>
</div>
</main>
);
Expand Down
1 change: 1 addition & 0 deletions apps/rsbuild-ssr-host/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@module-federation/runtime": "2.9.2",
"@module-federation/vite": "1.23.3",
"@tanstack/react-router": "1.170.41",
"@tanstack/router-core": "1.171.34",
"@tanstack/react-start": "1.168.60",
"example-host-context": "workspace:*",
"react": "19.3.0",
Expand Down
4 changes: 3 additions & 1 deletion apps/rsbuild-ssr-host/rsbuild.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ export default defineConfig({
dts: false,
name: "tanstack_rsbuild_ssr_host",
// Workspace dependencies declare "workspace:*", which is not a semver range.
shared: { "example-host-context": { requiredVersion: "*", singleton: true } },
shared: {
"example-host-context": { requiredVersion: "*", singleton: true },
},
shareStrategy: "loaded-first",
remotes: {
tanstack_rsbuild_ssr_remote:
Expand Down
7 changes: 7 additions & 0 deletions apps/rsbuild-ssr-host/src/remote.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@ declare module "tanstack_vite_remote/StatusCard" {
const StatusCard: ComponentType;
export default StatusCard;
}

declare module "tanstack_vite_remote/RouterCard" {
import type { ComponentType } from "react";

const RouterCard: ComponentType;
export default RouterCard;
}
5 changes: 5 additions & 0 deletions apps/rsbuild-ssr-host/src/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { RemoteBoundary } from "../components/RemoteBoundary";
// outage renders the remote once it is back.
const RsbuildStatusCard = lazyRemote(() => import("tanstack_rsbuild_ssr_remote/StatusCard"));
const ViteStatusCard = lazyRemote(() => import("tanstack_vite_remote/StatusCard"));
const ViteRouterCard = lazyRemote(() => import("tanstack_vite_remote/RouterCard"));

// Manifests of the remotes this route renders on the server.
const serverRenderedManifests = [
Expand All @@ -25,6 +26,7 @@ export const Route = createFileRoute("/")({
).then((lists) => lists.flat()),
import("tanstack_rsbuild_ssr_remote/StatusCard").catch(() => null),
import("tanstack_vite_remote/StatusCard").catch(() => null),
import("tanstack_vite_remote/RouterCard").catch(() => null),
]);
return { stylesheets };
},
Expand All @@ -51,6 +53,9 @@ function Home() {
<RemoteBoundary fallback="Loading the Vite remote…" name="Vite remote">
<ViteStatusCard />
</RemoteBoundary>
<RemoteBoundary fallback="Loading the Router-aware remote…" name="Router-aware remote">
<ViteRouterCard />
</RemoteBoundary>
</div>
</main>
);
Expand Down
1 change: 1 addition & 0 deletions apps/vite-host/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dependencies": {
"@module-federation/runtime": "2.9.2",
"@tanstack/react-router": "1.170.41",
"@tanstack/router-core": "1.171.34",
"@tanstack/react-start": "1.168.60",
"example-host-context": "workspace:*",
"react": "19.3.0",
Expand Down
7 changes: 7 additions & 0 deletions apps/vite-host/src/remote.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ declare module "tanstack_vite_remote/StatusCard" {
export default StatusCard;
}

declare module "tanstack_vite_remote/RouterCard" {
import type { ComponentType } from "react";

const RouterCard: ComponentType;
export default RouterCard;
}

declare module "tanstack_rsbuild_remote/StatusCard" {
import type { ComponentType } from "react";

Expand Down
6 changes: 6 additions & 0 deletions apps/vite-host/src/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { RemoteBoundary } from "../components/RemoteBoundary";
// Unlike React.lazy, lazyRemote loads again after a failure, so a server that hit a remote
// outage renders the remote once it is back.
const RemoteStatusCard = lazyRemote(() => import("tanstack_vite_remote/StatusCard"));
const RemoteRouterCard = lazyRemote(() => import("tanstack_vite_remote/RouterCard"));
const RsbuildStatusCard = lazyRemote(() => import("tanstack_rsbuild_remote/StatusCard"));
const RsbuildSsrStatusCard = lazyRemote(() => import("tanstack_rsbuild_ssr_remote/StatusCard"));

Expand All @@ -26,6 +27,7 @@ export const Route = createFileRoute("/")({
),
).then((lists) => lists.flat()),
import("tanstack_vite_remote/StatusCard").catch(() => null),
import("tanstack_vite_remote/RouterCard").catch(() => null),
import("tanstack_rsbuild_ssr_remote/StatusCard").catch(() => null),
]);
return { stylesheets };
Expand Down Expand Up @@ -62,6 +64,10 @@ function Home() {
<RemoteStatusCard />
</RemoteBoundary>

<RemoteBoundary fallback="Loading the Router-aware remote…" name="Router-aware remote">
<RemoteRouterCard />
</RemoteBoundary>

<RemoteBoundary fallback="Loading the Rsbuild SSR remote…" name="Rsbuild SSR remote">
<RsbuildSsrStatusCard />
</RemoteBoundary>
Expand Down
1 change: 1 addition & 0 deletions apps/vite-remote/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dependencies": {
"@module-federation/runtime": "2.9.2",
"@tanstack/react-router": "1.170.41",
"@tanstack/router-core": "1.171.34",
"@tanstack/react-start": "1.168.60",
"example-host-context": "workspace:*",
"react": "19.3.0",
Expand Down
25 changes: 25 additions & 0 deletions apps/vite-remote/src/components/RouterCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Link, useNavigate } from "@tanstack/react-router";
import "./StatusCard.css";

export default function RouterCard() {
const navigate = useNavigate();

return (
<article className="remote-card">
<p className="card-label">Router-aware remote</p>
<h2>Remote uses the host router</h2>
<p>
This exposed component reads the host&apos;s TanStack Router context through a shared router
runtime.
</p>
<p>
<Link to="/" hash="router-link">
Remote link
</Link>
</p>
<button type="button" onClick={() => void navigate({ to: "/", hash: "router-navigate" })}>
Remote navigate
</button>
</article>
);
}
5 changes: 4 additions & 1 deletion apps/vite-remote/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ export default defineConfig({
name: "tanstack_vite_remote",
dts: false,
// Workspace dependencies declare "workspace:*", which is not a semver range.
shared: { "example-host-context": { requiredVersion: "*", singleton: true } },
shared: {
"example-host-context": { requiredVersion: "*", singleton: true },
},
exposes: {
"./RouterCard": "./src/components/RouterCard.tsx",
"./StatusCard": "./src/components/StatusCard.tsx",
},
}),
Expand Down
14 changes: 8 additions & 6 deletions packages/tanstack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,11 @@ Defaults:

- `filename: "remoteEntry.js"`, `manifest: true`, and
`hostInitInjectLocation: "entry"`.
- `react` and `react-dom` are shared singletons. When the config declares
`remotes`, they are also `eager`, so the host's own React stays in the share
scope and a remote built by another bundler cannot replace it. Remote-only
builds stay lazy.
- `react`, `react-dom`, `@tanstack/react-router`, and `@tanstack/router-core`
are shared singletons. When the config declares `remotes`, they are also
`eager`, so the host's own React and Router runtimes stay in the share scope
and a remote built by another bundler cannot replace them. Remote-only builds
stay lazy.
- Any entry you pass in `shared` replaces the default for that package.

A Vite host renders before lazy shared modules resolve, so mark any other shared
Expand Down Expand Up @@ -129,8 +130,9 @@ can be consumed by Vite and Rsbuild hosts.

Defaults:

- `react` and `react-dom` are shared singletons, `eager` for hosts (configs with
`remotes`) and lazy for remote-only builds.
- `react`, `react-dom`, `@tanstack/react-router`, and `@tanstack/router-core`
are shared singletons, `eager` for hosts (configs with `remotes`) and lazy
for remote-only builds.
- `experiments.asyncStartup: true`, so entries wait for shared modules before
running. Pass `experiments: { asyncStartup: false }` to opt out.
- A remote's browser build uses `publicPath: "auto"`, so hosts on other origins
Expand Down
Loading
Loading