Replies: 4 comments 2 replies
-
|
This is a common issue with micro frontend architectures in Next.js. The JavaScript chunks fail to load because the asset prefix does not match between the host and the micro frontend. Root cause: When you deploy multiple Next.js apps as micro frontends, each app generates chunks with paths relative to its own base URL. The host app tries to load these chunks from its own domain, resulting in 404s. Solutions:
Could you share your |
Beta Was this translation helpful? Give feedback.
-
|
This is slightly deeper than just assetPrefix or CDN routing. In micro frontend setups like this, the real issue is that Next.js chunk loading depends on a consistent runtime origin context. When the host app rewrites requests to a different origin (catalog app), the initial HTML is rendered correctly, but the webpack runtime still tries to resolve _next/static chunks based on the host origin, not the micro frontend origin. That leads to:
That’s why you see symptoms like “button renders but onClick never fires”. assetPrefix alone is not enough here if the routing is done via rewrites instead of true navigation or proxy passthrough. What usually works in this architecture:
In practice, rewrites + separate Next.js apps breaks the assumption that the JS runtime origin == HTML origin. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
|
This is a classic Multi-Zone + asset prefix issue. Here's the complete solution: Root CauseWhen the host app proxies requests to the catalog app via Solution: Configure
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Summary
I am working on a Next.js micro frontend architecture using the App Router, next-intl, and Next.js 16.2.4.
The setup has multiple independent Next.js apps:
host app running on http://localhost:3000/
catalog app running on http://localhost:3001/
checkout app running on http://localhost:3002/
The host app is the main shell. It routes some paths to the catalog app using Next.js rewrites. The problem happens when the catalog app is accessed through the host app.
When I open a catalog route from the host app, the HTML/page renders, but the JavaScript does not load correctly. Because of this, client components are not hydrated and client-side actions do not work.
For example, a simple client component like this does not trigger the click event:
'use client';
function TestFile() {
const handleClick = () => {
console.log('clicked');
};
return (
Click me
);
}
export default TestFile;
The button renders, but console.log('clicked') does not run when clicking it.
Because hydration/client JS is not working, other JavaScript-based providers are also not working, such as:
context providers
cart/wishlist providers
toast provider
analytics provider
shared UI package client behavior
other interactive client components
Architecture
The host app uses Next.js rewrites to forward catalog routes to the catalog app.
Example from host/next.config.ts:
const catalogOrigin =
process.env.NEXT_PUBLIC_CATALOG_ORIGIN ||
process.env.CATALOG_ORIGIN ||
"http://localhost:3001/";
const nextConfig = {
async rewrites() {
return {
beforeFiles: [
{
source: "/:locale/catalog/:path*",
destination: ${catalogOrigin}/:locale/catalog/:path*,
},
{
source: "/:locale/all-products/:path*",
destination: ${catalogOrigin}/:locale/all-products/:path*,
},
{
source: "/:locale/category/:path*",
destination: ${catalogOrigin}/:locale/category/:path*,
},
{
source: "/:locale/product/:path*",
destination: ${catalogOrigin}/:locale/product/:path*,
},
{
source: "/:locale/wishlist/:path*",
destination: ${catalogOrigin}/:locale/wishlist/:path*,
},
],
};
},
};
The catalog app has its own assetPrefix:
const catalogOrigin =
process.env.NEXT_PUBLIC_CATALOG_ORIGIN ||
process.env.CATALOG_ORIGIN ||
"http://localhost:3001/";
const nextConfig = {
assetPrefix: catalogOrigin,
allowedDevOrigins: ["localhost:3000"],
transpilePackages: ["@package/ui", "@package/utils", "@package/analytics"],
};
Both apps use:
{
"next": "^16.2.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"next-intl": "^4.11.0"
}
Both apps use next-intl locale routing:
export const routing = defineRouting({
locales: ["en", "fr", "de"],
defaultLocale: "en",
});
The catalog app layout wraps pages with several client providers:
<Providers params={{ locale }} messages={messages}>
{children} The Providers component is a client component and includes providers like SessionProvider, NextIntlClientProvider, CartProvider, WishlistProvider, Toast, etc.
Expected Behavior
When I access a catalog route through the host app, for example:
http://localhost:3000/en/product/8078711455933
I expect:
the catalog page HTML to render
the catalog app JavaScript chunks to load
React hydration to complete
client components to work
onClick handlers to fire
providers and interactive UI behavior to work
Actual Behavior
The page renders, but client-side JavaScript does not work.
Symptoms:
onClick handlers do not fire
client actions do not work
providers depending on client JS do not initialize correctly
interactive UI from shared packages does not work
hydration appears to fail or never complete
In terminal logs, I also sometimes see errors like:
TypeError: Cannot read properties of undefined (reading 'call')
at .next/server/webpack-runtime.js
And when the catalog app is not running, the host shows proxy errors like:
Failed to proxy http://localhost:3001/en/product/8078711455933
ECONNREFUSED 127.0.0.1:3001
Additional information
Example
No response
Beta Was this translation helpful? Give feedback.
All reactions