Skip to content

gbeezus/shell-poc2

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

shell-poc2 — Module Federation host (POC 2)

Proof of concept for consuming a third-party React component at runtime via Webpack 5 Module Federation, with the same USWDS branding overlay used in POC 1 applied to the federated surface.

This is one of four repos in the POC set:

Repo Role Port
shell-poc1 CSS-overlay shell (POC 1) 3000
third-party-poc1 CSS-overlay target (POC 1) 3001
shell-poc2 (this) Module Federation host 3010
third-party-poc2 Module Federation remote 3011

The original Forum One starter docs are at README.nextjs.md and README.project.md.

Key finding — Module Federation + Next.js 16 App Router

The spec said to use @module-federation/nextjs-mf. That package declares peer dependency:

next@"^12 || ^13 || ^14 || ^15"

It does not support Next.js 16. Attempting to install against the starter (Next 16.2.3) produces an ERESOLVE error. There is no Next.js 16 adapter released as of the date of this POC.

This POC uses @module-federation/enhanced directly with custom webpack config, which does install. With it:

  • ✅ The remote produces a valid remoteEntry.js at http://localhost:3011/_next/static/chunks/remoteEntry.js.
  • ✅ The host's webpack config resolves the federated import at build time.
  • ✅ The host serves /tool with the dynamic import + error boundary client component.
  • ❌ At browser runtime, the federation share runtime throws loadShareSync errors trying to initialize the shared React copy. The symptom is Invalid hook call / Cannot read properties of null (reading 'useContext') — the classic two-Reacts problem when MF shares fail to deduplicate.

The root cause is the lack of a Next.js-specific federation wrapper for the App Router that knows how to set up async boundaries the way the runtime expects. The legacy nextjs-mf wrapper handled this for Pages Router; no equivalent exists yet for App Router + Next 16.

Recommended path forward (for the real engagement):

Option Implication
Both teams agree to Pages Router on Next ≤15 + @module-federation/nextjs-mf Federation works today, faithful to the spec. Pages Router is in long-term maintenance mode; App Router is the strategic direction.
Wait for App Router support in @module-federation/nextjs-mf or an equivalent wrapper No timeline guarantees.
Drop federation, use POC 1's CSS overlay + iframes for tool surfaces Falls back to a working, lower-coupling approach.

The recommendation lives in the parent plan, not this README. What this README documents is the technical reality the proposal is built on.

What's here

Everything except the actual cross-bundle React handshake is wired up:

  • next.config.jsModuleFederationPlugin host configuration. Federation runs client-only (server bundle aliases thirdparty/Tool to a no-op stub at lib/remote-stub.tsx). This shape is what you'd ship if the runtime path worked.
  • app/tool/page.tsx — server component that renders the federated tool inside the shell's USWDS chrome.
  • app/tool/RemoteTool.tsx — client component with next/dynamic({ ssr: false }) import of thirdparty/Tool, wrapped in an error boundary that surfaces a "Tool unavailable" fallback when the remote is unreachable or the runtime fails.
  • remote-modules.d.ts — TypeScript declaration so import 'thirdparty/Tool' typechecks without a real filesystem resolution.
  • public/brand/overrides.css — same USWDS DTCG-generated overrides as POC 1. Demonstrates that the CSS-overlay branding approach from POC 1 works identically here: the federated component would cascade against these tokens for free if it rendered.

Branding the federated component

Because the federated component mounts inside the host's DOM, it inherits the host's :root custom properties through normal CSS cascade. POC 1's mechanism applies unchanged — no extra federation-aware branding hook is needed. The <link rel="stylesheet" href="/brand/overrides.css"> in app/layout.tsx covers both the shell chrome and any federated component rendered inside it.

Auth handoff — documented patterns

The federated component runs inside the host's origin (it's bundled into client chunks served from :3010, not requested cross-origin per render). That means cookies are the host's cookies — same origin-scope. The remote's own API calls go to :3011 unless explicitly routed; that's the real trust boundary the auth design must cross.

Three propagation patterns to choose from:

  1. React context — host puts a Session into context, federated component reads it. Tight coupling: the context shape becomes a contract that both teams pin. Simplest to wire; brittle to schema drift.
  2. Token forwarding — host mints a short-lived JWT scoped to the remote's API and passes it via prop or Authorization header on the remote's fetches. Cleanest separation; requires both teams to agree on a token format (probably the same OIDC ID/access token).
  3. Cookie-shared subdomain — host sets the session cookie on .example.gov; remote API at tool.example.gov reads it. Easiest if the deployment topology supports it.

Federal-context note: PIV/CAC and ICAM-based SSO add session-refresh requirements that token-forwarding (pattern 2) handles better than context-sharing — context can go stale silently, whereas an expired token returns a 401 from the API and surfaces the refresh need.

What the third party must commit to: a contract for context shape or token format. Without that agreement, federation works visually but auth breaks.

Limitations of the federation approach (writeup-relevant)

  • Webpack-only. rspack/Turbopack support is still maturing — Next 16 defaults to Turbopack for dev (the Forum One starter explicitly passes --webpack to avoid this, which is the right move).
  • Version-locking shared deps. Host and remote must agree on major versions of React, ReactDOM, and Next. POC 2 declares singleton: true, requiredVersion: false; production should pin to a specific version range.
  • SSR with App Router is the rough edge. ssr: false is the pragmatic POC choice. It costs SEO and time-to-paint on the federated route.
  • Failure modes when the remote is down. The error boundary in RemoteTool.tsx handles this — without it, a crashloop would surface as a blank tool route.
  • Deploy coupling. A breaking change in the remote's exposed interface breaks the host at runtime, not build time. Needs contract testing (e.g., schema-check the federated component's prop interface in CI).

Run

# Terminal 1 — start the remote first so the host can load remoteEntry.js
cd ../third-party-poc2
npm install
npm run dev   # http://localhost:3011

# Terminal 2 — start the host
cd ../shell-poc2
npm install
npm run dev   # http://localhost:3010

Visit http://localhost:3010/tool and observe the browser console — the finding above is reproducible.

Render.com deploy

render.yaml is committed. To deploy, push to GitHub, connect in Render, accept the blueprint. Set NEXT_PUBLIC_REMOTE_URL in Render env to point at the deployed third-party-poc2 URL's remoteEntry.js path.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Generated from forumone/nextjs-project