Published: April 29, 2026 · 17 min read
Mixing React and Next.js Micro Frontends Together: Hybrid Architecture Guide
You have a Next.js host that needs to render /products and /content with server-side rendering for SEO, while /login, /cart, /account, and /support are interactive client-only experiences. Building everything in Next.js wastes a Node.js process and 200 MB of RAM per page that does not need SSR. Building everything in React loses SEO on the pages that need it. The answer is mixing React and Next.js micro frontends in a single host application — using ModuleFederationPlugin for client-only React remotes and NextFederationPlugin for SSR-capable Next.js remotes. This article shows the complete hybrid architecture, the exact dual-plugin host config, and the deployment differences between the two remote types. In the previous article, you learned why Next.js needs two remoteEntry.js files and the isServer flag — now you will see how that same flag lets one host load remotes built with either plugin.
In this guide, you will:
- See the full hybrid architecture diagram with React and Next.js remotes side by side
- Configure the Next.js host to load 4 React remotes and 2 Next.js remotes from one
next.config.js - Compare ModuleFederationPlugin vs NextFederationPlugin remote outputs and URL patterns
- Wire up real host pages that consume both remote types using
next/dynamicwithssr: false - Share a single Redux singleton across both remote types using
@myapp/store - Set up an Nginx reverse proxy that serves React remotes statically and routes Next.js remotes to Node.js
- Avoid the 8 most common pitfalls when mixing remote types

Why Mix React and Next.js Micro Frontends?
Not every page in a large application needs server-side rendering. Pages like /products/[slug] and /faq benefit massively from SSR — search engines crawl pre-rendered HTML for product titles, prices, and FAQ content. Pages like /login, /bag, and /profile are interactive client-only experiences — their content depends on localStorage auth tokens and Redux state that only exists in the browser. Forcing those interactive pages through Next.js means running a Node.js process per remote that adds memory pressure for zero SEO benefit.
A hybrid architecture splits the application by rendering need:
- Next.js remotes (SSR + CSR) for pages where Google needs to see real HTML — Products, Content (FAQ, Privacy Policy, Terms, Size Guide).
- React remotes (CSR only) for pages that are interactive, auth-gated, or behind a login wall — Auth, Cart, Account, Support.
This mirrors how a real e-commerce frontend works. The home page, category pages, and product detail pages must rank in Google. The cart, checkout, and account pages do not. Mixing the two remote types lets each page use the right tool without forcing one architecture across the entire app.
Hybrid is also a migration strategy. If you already have a React MFE monorepo built with Webpack 5 — like the one in the React MFE monorepo guide — you can introduce Next.js for new SSR-heavy pages without rewriting the existing React remotes. The Next.js host loads them as-is.
Hybrid Architecture — One Host, Two Plugin Types
A single Next.js host loads both remote types simultaneously. The remote URL pattern in the host's next.config.js tells Module Federation which plugin built each remote. The runtime negotiates shared dependencies across all of them — React, Redux, and the shared @myapp/store package are loaded once and reused by every remote regardless of its plugin.
The key insight is that the host does not care how the remote was built. It only cares about three things: the remote's name, its remoteEntry.js URL, and its shared dependency list. As long as the shared list matches across the host and the remote, Module Federation handles the rest.
Remote URL Pattern — How the Host Tells Them Apart
The host config distinguishes the two remote types entirely through the URL string. There is no flag, no plugin-specific option, and no separate plugin instance — just a different URL pattern.
| Pattern Element | React Remote (Auth) | Next.js Remote (Products) |
|---|---|---|
| Plugin | ModuleFederationPlugin | NextFederationPlugin |
| Config file | webpack.config.js | next.config.js |
| Output mode | static dist/ folder | .next/standalone + .next/static |
| Public path | publicPath: '/auth/' | basePath + assetPrefix: '/products' |
| Filename pattern | [name].[contenthash].js | _next/static/chunks/[name]-[hash].js |
| remoteEntry location | /auth/remoteEntry.js | /products/_next/static/chunks/remoteEntry.js |
| SSR remoteEntry | NOT generated | /products/_next/static/ssr/remoteEntry.js |
| Server runtime | None — static files | Node.js (server.js standalone) |
| Deployment target | Nginx static hosting | PM2 / Docker / Kubernetes |
| Routing | Host-controlled (props) | basePath isolates routes |
| Host import | import('Auth/Login') | import('Content/FAQ') |
isServer ternary | NOT needed | Required in remotes config |
| Bootstrap | Manual bootstrap.js OR automaticAsyncBoundary | automaticAsyncBoundary: true |
| Image loader | Plain img tags or external | next/image with enableImageLoaderFix |
Notice the two columns are completely different — different file paths, different runtimes, different deployment targets. Yet the host configuration uses the same remotes: object for both.
The Dual-Plugin Host Configuration
Here is the complete next.config.js for the host. It uses one NextFederationPlugin instance to declare all six remotes — four React (Auth, Account, Cart, Support) and two Next.js (Content, Products).
The crucial line is the remotes block. The four React remotes get plain /[name]/remoteEntry.js URLs because they were built with ModuleFederationPlugin which only emits one entry. The two Next.js remotes use the isServer ternary because NextFederationPlugin generates two entries — one for the Node.js server build, one for the browser build.
Do not put the isServer ternary on React remote URLs. React remotes have no _next/static/ssr/ folder — that path returns 404. The server build then fails to fetch the remote, and the host crashes with a build error. Plain /[name]/remoteEntry.js only.
React Remote Configuration — ModuleFederationPlugin
Each React remote uses Webpack 5 Module Federation (opens in a new tab) via ModuleFederationPlugin. Production and local development configs differ significantly — production uses content hashes for long-lived caching while local development uses HTTPS dev server with CORS headers and disables code splitting for fast HMR.
Local development and production webpack configs are different. In dev, the remote runs on its own HTTPS port (e.g., https://localhost:4101) with CORS headers. In production, the remote is built once and served as static files from a path like /auth/.
The Auth remote exposes Login, OTPVerify, and Register — three components the host pulls in when the user navigates to /login, /otpverify, or /register. Cart, Account, and Support follow the same pattern with their own exposed components and their own publicPath (/cart/, /account/, /support/).
Next.js Remote Configuration — NextFederationPlugin
Each Next.js remote uses NextFederationPlugin from @module-federation/nextjs-mf and declares basePath plus assetPrefix to scope all of its routes and static assets under a unique path. Without basePath, two Next.js remotes would both write main-[hash].js to /_next/static/chunks/ and overwrite each other.
output: 'standalone' is essential — it produces a self-contained Node.js bundle inside .next/standalone/ that includes only the dependencies the app actually uses. That bundle is what gets deployed to the Node.js server (or container). For full details on basePath and assetPrefix, see the Next.js Remote MFE basePath guide.

How the Host Consumes a React Remote
The host loads the React Auth/Login component using next/dynamic with ssr: false. The wrapper page passes a navigation callback into the remote — the remote does not import next/navigation directly, keeping it framework-agnostic.
The bridge pattern (onNavigate prop) is critical for hybrid setups. The Auth remote was originally built for a React-only host that uses react-router-dom, not next/navigation. By passing the router callback in via props, the same Auth remote runs unmodified inside any host — Next.js, Create React App, or a Vite-based shell.
How the Host Consumes a Next.js Remote
The host loads the Content/FAQ component the same way — next/dynamic with ssr: false. The remote is imported by name (Content/FAQ), and Module Federation resolves it to the exposed component declared in the Content remote's next.config.js.
Even though the Content remote has an /ssr/remoteEntry.js, the host still uses ssr: false. The reason: the FAQ component reads from the shared Redux store (which only exists on the client), and rendering it on the server would either crash or trigger a hydration mismatch. The /ssr/ entry exists so that Next.js's server build can import the federated module without crashing — not so that the component renders on the server. See the SSR vs CSR guide for the full explanation.
Shared Dependencies — Same Singleton, Both Plugins
Module Federation negotiates shared dependencies across all remotes regardless of which plugin built them. As long as both ModuleFederationPlugin and NextFederationPlugin declare the same package as a singleton, the host's instance is the one that gets used.
This is why a single Redux store works across both remote types. The shared @myapp/store package is declared with singleton: true, strictVersion: true, requiredVersion: '1.0.0' in the host, in every React remote's webpack.config.js, and in every Next.js remote's next.config.js. Module Federation enforces that exactly one instance loads at runtime.
For the deeper Module Federation singleton mechanics, see Shared Dependencies and Singleton Pattern. For the Redux-specific patterns across MFEs, the Shared Redux Store article covers the Provider setup.
Deployment — Static Files vs Node.js Servers
The two remote types have completely different deployment models. React remotes are static files behind Nginx. Next.js remotes are long-lived Node.js processes behind a reverse proxy.
| Aspect | React Remote (Auth, Cart, Account, Support) | Next.js Remote (Products, Content) |
|---|---|---|
| Build command | webpack --config webpack.config.js | next build |
| Output | dist/ folder of static JS/CSS | .next/standalone/ Node.js app + .next/static/ |
| Runtime | None — pure static files | Node.js server (server.js entry) |
| Hosting | Nginx serving static files at /auth/* | PM2 or Docker container exposing port |
| Process count | 1 Nginx instance handles all | 1 Node.js process per Next.js remote |
| Memory footprint | ~0 (static files) | ~80–200 MB per remote |
| Startup time | Instant (no boot) | 1–3 seconds (Next.js cold start) |
| Update strategy | Replace dist files behind atomic symlink | Rolling deploy via PM2 reload or k8s |
| Caching | Long-lived contenthash filenames | Next.js handles cache headers automatically |
| HTTPS | Nginx terminates TLS | Behind Nginx reverse proxy (recommended) |
| Logs | Nginx access logs | Next.js stdout + PM2 logs |
| Health check | HTTP 200 on /auth/remoteEntry.js | HTTP 200 on /products/api/health |
A typical hybrid deployment uses one Nginx instance as the public entry point. Nginx serves the React remotes directly (/auth/, /cart/, /account/, /support/ are filesystem aliases) and reverse-proxies the Next.js remotes (/products/, /content/) to PM2-managed Node.js processes. The host (Main) is itself a Next.js standalone server running on port 3000.
This configuration is what makes the entire architecture work behind a single domain. The browser sees dev.myapp.com for everything — the routing happens server-side. For more on production Nginx for hybrid MFE deployments, Martin Fowler's Micro Frontends article (opens in a new tab) covers the architectural reasoning, and the Next.js standalone documentation (opens in a new tab) covers the standalone build.

Common Pitfalls When Mixing Remote Types
After months of running a hybrid React + Next.js architecture in production, eight specific pitfalls account for the majority of broken deployments. Every one of them comes down to confusing the two plugin types or the two URL patterns.
Most of these only surface in production — local development with self-signed certs and localhost URLs masks the routing issues. Always test the full URL pattern in a staging environment before deploying.
Hybrid Architecture Decision Flow
Before building a new feature, decide which remote type fits — the answer is almost always one of these:
| Page Type | Remote Type | Why |
|---|---|---|
| Public catalog, SEO-critical (products, blog, FAQ) | Next.js remote | Server-rendered HTML for search engines |
| Auth-gated client interactions (cart, profile, settings) | React remote | No SEO need, faster build, no Node.js process |
| Marketing landing pages with content updates | Next.js remote | ISR for content updates without redeploy |
| Admin dashboards behind login | React remote | Static files, instant deploy, low memory |
| Real-time / WebSocket-heavy pages | React remote | No SSR overhead, full client control |
| Content pages (terms, privacy, FAQ) | Next.js remote | SSR for crawlers + ISR for editorial updates |
The general rule: if Google does not need to read it, build it as a React remote.
What's Next
You now understand how to build a hybrid micro frontend architecture with one Next.js host loading both React and Next.js remotes — the dual-plugin configuration, the URL pattern differences, the bridge pattern for cross-framework navigation, the shared singleton dependencies, and the Nginx reverse proxy that ties it all together. The next article covers shared Redux store in Next.js micro frontends — the Provider setup, the strictVersion config, and the SSR hydration gotchas you hit when the same store is consumed by both React and Next.js remotes.
← Back to SSR vs CSR in Next.js Module Federation
Continue to Shared Redux Store in Next.js Micro Frontends →
Frequently Asked Questions
Can a Next.js host load both React and Next.js remotes at the same time?
Yes. NextFederationPlugin in the host's next.config.js can declare remotes built with either ModuleFederationPlugin (React) or NextFederationPlugin (Next.js). The remote URL pattern tells the host which type each remote is — plain /remoteEntry.js for React remotes and /_next/static/{ssr|chunks}/remoteEntry.js for Next.js remotes. The host loads each remote with next/dynamic and ssr: false, and Module Federation negotiates shared dependencies across all of them as singletons. This hybrid pattern is the foundation of incremental migration — old React MFEs keep working while new pages are built with Next.js for SSR.
Why do React remotes have only one remoteEntry.js while Next.js remotes have two?
React remotes built with ModuleFederationPlugin run exclusively in the browser — there is no server build, so a single remoteEntry.js is generated for the browser. Next.js remotes use NextFederationPlugin which hooks into the dual webpack build that Next.js runs (server + client). NextFederationPlugin emits one remoteEntry.js for the Node.js server build at _next/static/ssr/remoteEntry.js and another for the browser build at _next/static/chunks/remoteEntry.js. The host uses the isServer flag in next.config.js to pick the correct path during each webpack pass.
Why must I set ssr: false on every remote import even for Next.js remotes?
Every remote — React or Next.js — must be loaded with ssr: false in next/dynamic because Module Federation containers attach to the window object and cannot run during the Node.js server render. Even Next.js remotes that have an /ssr/ remoteEntry.js still depend on browser-only state (Redux store created on mount, useEffect data fetching, localStorage auth tokens). Without ssr: false, the host crashes with ReferenceError: window is not defined or hits a hydration mismatch the moment the client takes over. The /ssr/ entry exists for cases where Next.js needs to import the federated module during the server build — not for rendering remote components on the server.
How do I navigate between pages from inside a React remote when the host is Next.js?
Use the bridge pattern. The host passes a callback prop (e.g., onNavigate) into the React remote, and the remote calls it instead of importing next/navigation directly. The host's wrapper page uses useRouter from next/navigation and forwards the path string to router.push. This keeps the React remote framework-agnostic — it has no Next.js dependency and can be loaded by any host (Next.js, Create React App, Vite). If the React remote imports next/navigation directly, its standalone build breaks because Next.js is not in its package.json, and even if installed, the router context only exists when the component runs inside a Next.js host.
Why do Next.js remotes need basePath and assetPrefix but React remotes do not?
Next.js remotes write static assets to /_next/static/chunks/ by default. If two Next.js remotes ran on the same domain without basePath, they would both try to write main-abc.js to the same /_next/static/chunks/ path — assets from one remote would overwrite the other's. basePath and assetPrefix scope every Next.js remote's routes and assets under a unique path (e.g., /products and /content), preventing collisions. React remotes do not face this problem because their webpack output is fully customized via publicPath: '/auth/' and the host serves each one from its own /auth/, /cart/, /account/ folder via Nginx. There is no shared /_next/ directory to collide on.
Can I share a Redux store between React and Next.js remotes in the same host?
Yes. Declare the store package (e.g., @myapp/store) as a singleton with strictVersion: true and requiredVersion: '1.0.0' in the shared config of the host AND every remote — both ModuleFederationPlugin and NextFederationPlugin support the same shared API. The host creates the store once inside a ClientReduxProvider component, and Module Federation negotiates that the store package is already loaded when each remote requests it. Both React and Next.js remotes import from @myapp/store and receive the SAME store instance. This makes it possible for the Auth React remote to call dispatch(setUser()) and the Products Next.js remote to read useSelector(state => state.auth.user) and see the logged-in user.
How does deployment differ between React remotes and Next.js remotes?
React remotes built with ModuleFederationPlugin produce a dist/ folder of static JS, CSS, and HTML files served directly by Nginx — no runtime process is needed. They have near-zero memory footprint and instant startup. Next.js remotes built with NextFederationPlugin and output: 'standalone' produce a Node.js server bundle (server.js + .next/static/) that must run as a long-lived process — typically managed by PM2 or in a container with Kubernetes. Each Next.js remote consumes 80–200 MB of RAM and takes 1–3 seconds to cold start. In a hybrid setup, Nginx serves React remotes statically from /auth/, /cart/, /account/, /support/ paths and reverse-proxies Next.js remote paths /products/ and /content/ to their respective Node.js processes.