cd /news/developer-tools/next-js-turbopack-module-federation-… · home › topics › developer-tools › article
[ARTICLE · art-160896] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=· neutral

Next.js Turbopack Module Federation in 2026: Sharing Code Across Micro-Frontends Without Webpack

A developer working with Next.js micro-frontends found that webpack's Module Federation plugin breaks under Turbopack, since Turbopack does not expose webpack's container format or runtime APIs, causing remote modules to fail at build time with "Cannot find module" errors. The proposed fix reframes module federation as a deployment pattern: Next.js multi-zones share a domain and coordinate routing via rewrites, with shared dependencies resolved at build time through package resolution rather than runtime module containers. The tradeoff is better isolation and independent deployment at the cost of duplicated dependencies and larger JavaScript payloads.

by read16 min views1 publishedOct 30, 2026

This article was written with the assistance of AI, under human supervision and review.

Module federation in Next.js evolved from a webpack-specific pattern into a build-system-agnostic architecture problem. Organizations that built micro-frontend systems on webpack's Module Federation plugin now face a choice: rewrite for Turbopack's bundling model or adopt Next.js multi-zones with shared dependencies. The transition exposes assumptions about runtime module sharing that developers rarely questioned until the bundler changed.

Teams discover the issue when next dev --turbo replaces their development workflow. The webpack-based Module Federation configuration stops working because Turbopack does not expose webpack's container format or runtime APIs. A micro-frontend that loaded remote modules from sibling applications now fails at build time with "Cannot find module" errors. The application compiles when switching back to webpack with next dev --webpack, but production deployments target Turbopack for its speed improvements.

The solution requires rethinking module federation as a deployment pattern rather than a bundler feature. Next.js multi-zones provide the architectural foundation: separate applications that share a domain and coordinate routing. Shared dependencies move from runtime module containers to build-time package resolution. Remote modules become deployed zones that expose routes instead of JavaScript bundles. The host application routes requests to zone applications using rewrites, and zones handle their own bundling independently.

This architectural shift delivers better isolation and simpler deployment at the cost of runtime module sharing. Each zone bundles its own dependencies, which increases total JavaScript payload when zones duplicate packages. The tradeoff favors maintainability because zones deploy independently without coordinating dependency versions at runtime.

Module federation in webpack allows separate builds to share modules at runtime. A host application loads remote modules from deployed containers without including them in its own bundle. The ModuleFederationPlugin generates container entry points that expose selected modules and define shared dependencies. When the host application imports a federated module, webpack's runtime resolves the remote container URL and loads the module dynamically.

Next.js applications use this pattern by configuring webpack in next.config.js. The configuration defines exposed modules, shared dependencies, and remote container locations. A typical setup includes a host application that consumes modules from multiple remote applications.

// next.config.js for host application with webpack module federation
const { NextFederationPlugin } = require('@module-federation/nextjs-mf');

module.exports = {
  webpack(config, options) {
    config.plugins.push(
      new NextFederationPlugin({
        name: 'host',
        remotes: {
          remote1: `remote1@${process.env.REMOTE1_URL}/_next/static/chunks/remoteEntry.js`,
          remote2: `remote2@${process.env.REMOTE2_URL}/_next/static/chunks/remoteEntry.js`,
        },
        shared: {
          react: { singleton: true, requiredVersion: '^18.0.0' },
          'react-dom': { singleton: true, requiredVersion: '^18.0.0' },
        },
      })
    );
    return config;
  },
};

The remote applications configure exposed modules that the host can import. Each remote builds a container that exports selected components and resolves shared dependencies according to the federation config.

// next.config.js for remote application
const { NextFederationPlugin } = require('@module-federation/nextjs-mf');

module.exports = {
  webpack(config, options) {
    config.plugins.push(
      new NextFederationPlugin({
        name: 'remote1',
        filename: 'static/chunks/remoteEntry.js',
        exposes: {
          './ProductCard': './components/ProductCard',
          './CheckoutButton': './components/CheckoutButton',
        },
        shared: {
          react: { singleton: true },
          'react-dom': { singleton: true },
        },
      })
    );
    return config;
  },
};

Runtime module sharing depends on webpack's container format and initialization sequence. The host application loads remote containers by injecting script tags or using dynamic imports. Webpack's runtime coordinates dependency resolution to ensure shared modules load exactly once. This works reliably when all applications use webpack with compatible container versions.

The webpack-specific implementation creates tight coupling between the bundler and the module federation architecture. Teams structure their micro-frontends around webpack's container format, which becomes a dependency that cannot be swapped without rewriting the entire system.

Module federation architecture diagram showing webpack containers

Turbopack replaces webpack as Next.js 16's default bundler. The architecture uses Rust-based incremental compilation with persistent caching, which delivers faster builds than webpack's JavaScript-based pipeline. Turbopack compiles only changed modules during development and caches compilation results to disk. Build times improve by 10x for large applications according to Vercel's benchmarks (Next.js 16 release notes, October 2026).

The bundler does not implement webpack's Module Federation plugin or container format. Turbopack's module system differs fundamentally from webpack's runtime architecture. Webpack generates container entry points that expose modules through a global object. Turbopack outputs ES modules with static import/export semantics that resolve at build time. There is no runtime container API to load federated modules dynamically.

Next.js 16 with Turbopack removes support for the webpack() configuration key in next.config.js. Applications that relied on webpack plugins for module federation no longer have access to the container plugin. The next dev and next build commands default to Turbopack, and the --webpack flag provides a temporary escape hatch for compatibility.

The architectural shift forces teams to reconsider how they structure micro-frontends. Runtime module federation assumed a bundler would coordinate module across deployed applications. Turbopack's design rejects that pattern in favor of build-time optimization and static analysis. Applications must adopt deployment-based federation where routing replaces runtime module containers.

Turbopack supports webpack s through a compatibility layer in the turbopack.rules configuration (Next.js Turbopack API reference, v16.1). This allows some webpack-based tooling to continue working, but does not extend to runtime plugins like Module Federation. The compatibility targets file transformations during compilation, not runtime module resolution.

The transition creates pressure to adopt alternative patterns that work with Turbopack's constraints. Next.js multi-zones become the primary approach for code sharing across micro-frontends. Each zone deploys as an independent application with its own Turbopack build. Zones coordinate through routing rather than module containers.

Multi-zones in Next.js provide the architectural replacement for webpack module federation. Each zone is a separate Next.js application that serves a subset of routes on a shared domain. The host application acts as the router, forwarding requests to zone applications based on URL paths. Zones deploy independently and bundle their dependencies without runtime coordination.

The setup requires configuring each zone with an assetPrefix to avoid static file conflicts. The host application defines rewrites that route paths to zone domains. A typical configuration includes three applications: a host that handles the root paths, and two zones for specialized features.

// next.config.ts for a zone application (e.g., product catalog)
import type { NextConfig } from 'next';

const config: NextConfig = {
  assetPrefix: '/catalog-static',
  async rewrites() {
    return {
      beforeFiles: [
        {
          source: '/catalog-static/_next/:path+',
          destination: '/_next/:path+',
        },
      ],
    };
  },
};

export default config;

The host application configures rewrites to forward zone paths to their deployed domains. This routes user requests to the correct application without exposing the zone URLs in the browser.

// next.config.ts for host application
import type { NextConfig } from 'next';

const config: NextConfig = {
  async rewrites() {
    return [
      {
        source: '/catalog',
        destination: `${process.env.CATALOG_ZONE_URL}/catalog`,
      },
      {
        source: '/catalog/:path+',
        destination: `${process.env.CATALOG_ZONE_URL}/catalog/:path+`,
      },
      {
        source: '/catalog-static/:path+',
        destination: `${process.env.CATALOG_ZONE_URL}/catalog-static/:path+`,
      },
      {
        source: '/checkout',
        destination: `${process.env.CHECKOUT_ZONE_URL}/checkout`,
      },
      {
        source: '/checkout/:path+',
        destination: `${process.env.CHECKOUT_ZONE_URL}/checkout/:path+`,
      },
      {
        source: '/checkout-static/:path+',
        destination: `${process.env.CHECKOUT_ZONE_URL}/checkout-static/:path+`,
      },
    ];
  },
};

export default config;

Shared code moves from runtime module federation to build-time package references. A monorepo structure with workspaces allows zones to import shared components and utilities as npm packages. Each zone includes shared packages as dependencies and Turbopack bundles them at build time.

// package.json for zone application in monorepo
{
  "name": "@company/catalog-zone",
  "dependencies": {
    "@company/ui-components": "workspace:*",
    "@company/auth-utils": "workspace:*",
    "next": "16.1.0",
    "react": "^18.3.1",
    "react-dom": "^18.3.1"
  }
}

Zones import shared components using standard ES module imports. Turbopack resolves workspace packages during compilation and includes them in each zone's bundle.

// app/catalog/page.tsx in catalog zone
import { ProductCard } from '@company/ui-components';
import { useAuth } from '@company/auth-utils';

export default function CatalogPage() {
  const { user } = useAuth();

  return (
    <div>
      <h1>Product Catalog</h1>
      {user && <p>Welcome, {user.name}</p>}
      <ProductCard
        title="Example Product"
        price={29.99}
        imageUrl="/products/example.jpg"
      />
    </div>
  );
}

This setup eliminates runtime module containers and version coordination. Each zone bundles its dependencies independently, which increases total payload size when zones duplicate packages. The tradeoff favors deployment independence and simpler dependency management.

Multi-zone architecture with shared packages

Remote module consumption in multi-zones requires explicit routing configuration instead of dynamic imports. The host application cannot load zone components directly because zones deploy as separate Next.js applications. Navigation between zones performs hard page loads that unload the current zone's resources and load the new zone.

Zones expose routes rather than component exports. A zone serves pages at specific paths, and the host application routes users to those paths through rewrites or standard links. The browser treats navigation to a zone as a navigation to a different part of the same domain.

// app/page.tsx in host application
import Link from 'next/link';

export default function HomePage() {
  return (
    <div>
      <h1>Welcome</h1>
      <nav>
        <Link href="https://dev.to/catalog">Browse Products</Link>
        <Link href="https://dev.to/checkout">Checkout</Link>
      </nav>
    </div>
  );
}

Clicking the link to /catalog triggers a hard navigation to the catalog zone. The host application's rewrite configuration forwards the request to the catalog zone's domain, and the browser loads the catalog zone's JavaScript bundle. This differs from webpack module federation where a federated component could render within the host's page without a navigation.

Shared data patterns require server-side coordination or external state. Zones cannot share React state or context across zone boundaries because each zone runs its own React root. Authentication tokens, user preferences, and cart data must persist in cookies, localStorage, or a backend API that all zones can access.

// lib/auth.ts shared package used by all zones
import { cookies } from 'next/headers';

export async function getUser() {
  const cookieStore = await cookies();
  const token = cookieStore.get('auth_token');

  if (!token) {
    return null;
  }

  const response = await fetch(`${process.env.API_URL}/user`, {
    headers: { Authorization: `Bearer ${token.value}` },
  });

  return response.ok ? response.json() : null;
}

Each zone calls getUser() independently to fetch user data from the backend. The function lives in a shared package that all zones import, but execution happens separately in each zone's server runtime.

Soft navigation within a zone uses Next.js's Link component normally. Navigating between pages in the same zone performs client-side routing without re the application. Hard navigation only occurs when crossing zone boundaries.

The pattern forces explicit boundaries between micro-frontends. Zones must define clear responsibilities and minimize cross-zone dependencies. A catalog zone handles product browsing and search. A checkout zone manages cart and payment. The host application provides navigation and shared layout elements.

Aspect Webpack Module Federation Turbopack Multi-Zones
Build time 60-120 seconds for host + remotes 5-15 seconds per zone (parallel builds)
Initial page load 180-250 KB (host + federation runtime) 120-180 KB per zone (no federation overhead)
Subsequent zone navigation 80-120 KB (remote container + modules) 120-180 KB (full page with new bundle)
Shared dependency handling Runtime version negotiation Build-time resolution (duplicated across zones)
Hot module reload 2-5 seconds (webpack HMR) 50-500 milliseconds (Turbopack incremental)
Deploy coordination Must align dependency versions across remotes Independent zone deploys

Webpack module federation delivers smaller initial payloads when multiple remotes share dependencies. The federation runtime loads React once and reuses it across all federated components. Each remote module adds only its unique code to the total JavaScript size. This optimization matters when a host application loads five or more remote modules from different teams.

Turbopack multi-zones duplicate shared dependencies across zone bundles. If three zones each use React, React ships three times. The duplication increases total JavaScript by 40-60 KB per additional zone for typical React applications. The cost compounds with additional shared dependencies like state management libraries or UI frameworks.

Build performance favors Turbopack by a significant margin. Each zone builds independently in parallel, and Turbopack's incremental compilation completes in seconds. Webpack module federation requires coordinating builds across all federated applications and waiting for the slowest remote to finish. Large organizations with 10-15 micro-frontends see build pipelines complete 5-10x faster with multi-zones.

Development experience improves with Turbopack's hot module reload speed. Changes to zone code reflect in the browser within 500 milliseconds. Webpack HMR takes 2-5 seconds for equivalent updates in federated applications. The difference compounds during feature development where developers make hundreds of small changes per day.

The navigation model trades client-side routing for simpler architecture. Webpack module federation loads remote modules without page navigation, which feels faster to users moving between micro-frontends. Multi-zones perform hard navigations that reload the entire page. The perceived slowdown disappears for applications where zones represent distinct user workflows with infrequent transitions.

Version coordination becomes simpler with multi-zones. Each zone deploys independently with its own dependency versions. Breaking changes in shared packages affect only the zones that have not upgraded. Webpack module federation requires all remotes to coordinate React and shared library versions to avoid runtime conflicts. The coordination overhead grows with team count.

Shared dependencies in multi-zones require build-time coordination through package versioning. Monorepo workspaces allow zones to depend on shared packages that reference the same version. Breaking changes in shared code require updating all consuming zones before deployment.

// packages/ui-components/package.json
{
  "name": "@company/ui-components",
  "version": "3.2.0",
  "peerDependencies": {
    "react": "^18.3.0",
    "react-dom": "^18.3.0"
  }
}

Zones specify workspace dependencies using the workspace:* protocol. This ensures all zones use the same version of shared packages without duplicating package code in the repository.

// apps/catalog-zone/package.json
{
  "dependencies": {
    "@company/ui-components": "workspace:*",
    "@company/analytics": "workspace:*"
  }
}

Version conflicts surface at build time rather than runtime. If a zone attempts to import an incompatible shared package version, the build fails with a clear error message. This prevents the subtle runtime failures that occur with webpack module federation when remotes load conflicting dependency versions.

Error boundaries protect against zone failures at the routing level. If a zone crashes or fails to load, the host application can display a fallback UI instead of a broken page. This requires implementing error handling in the zone's root layout.

// app/layout.tsx in zone application
import { ErrorBoundary } from '@company/ui-components';

export default function ZoneLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <ErrorBoundary
      fallback={
        <div>
          <h1>Something went wrong</h1>
          <p>We are working to fix this issue.</p>
          <a href="https://dev.to/">Return to home</a>
        </div>
      }
    >
      {children}
    </ErrorBoundary>
  );
}

The error boundary catches React errors within the zone and renders the fallback UI. This prevents a crashed zone from breaking the entire application. Users can navigate back to the host or other zones even when one zone fails.

Network errors during zone navigation require server-side fallbacks. If a zone's server becomes unavailable, the host application's rewrite returns a 502 error. Next.js middleware can intercept these errors and redirect to a maintenance page.

// middleware.ts in host application
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export async function middleware(request: NextRequest) {
  const zonePaths = ['/catalog', '/checkout'];
  const isZonePath = zonePaths.some(path => 
    request.nextUrl.pathname.startsWith(path)
  );

  if (isZonePath) {
    try {
      const response = await fetch(request.url, {
        method: request.method,
        headers: request.headers,
      });

      if (!response.ok) {
        return NextResponse.redirect(new URL('/maintenance', request.url));
      }

      return response;
    } catch (error) {
      return NextResponse.redirect(new URL('/maintenance', request.url));
    }
  }

  return NextResponse.next();
}

Observability for multi-zones requires tracing requests across zone boundaries. Each zone should emit structured logs that include a request ID propagated through rewrites. The host application generates the request ID and passes it to zones via headers.

// middleware.ts in host application
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { randomUUID } from 'crypto';

export function middleware(request: NextRequest) {
  const requestId = randomUUID();
  const requestHeaders = new Headers(request.headers);
  requestHeaders.set('x-request-id', requestId);

  return NextResponse.next({
    request: { headers: requestHeaders },
  });
}

Zones extract the request ID from headers and include it in log output. This allows correlating errors across zone boundaries when debugging production issues.

Dependency optimization reduces bundle duplication when multiple zones share heavy libraries. The optimizePackageImports option in next.config.ts tells Turbopack to tree-shake package imports, which eliminates unused code from bundles (Next.js package bundling guide, v16.1).

// next.config.ts for zone application
import type { NextConfig } from 'next';

const config: NextConfig = {
  experimental: {
    optimizePackageImports: ['@mui/material', 'lodash', 'date-fns'],
  },
};

export default config;

This configuration reduces bundle size by 20-40 KB for zones that import only a subset of package exports. The optimization applies per zone, which means each zone still bundles its own copy of shared packages but with only the required code.

Server actions require explicit origin allowlists when zones share a domain. Next.js validates the origin header for server action requests to prevent CSRF attacks. The host domain must appear in each zone's allowedOrigins configuration (Next.js server actions configuration, v16.0).

// next.config.ts for zone application
import type { NextConfig } from 'next';

const config: NextConfig = {
  experimental: {
    serverActions: {
      allowedOrigins: ['example.com', 'www.example.com'],
    },
  },
};

export default config;

Without this configuration, server actions in zones fail with CSRF errors when the zone serves requests proxied through the host domain.

No. Turbopack does not implement webpack's container format or runtime APIs. Applications using webpack module federation must either continue using webpack with the --webpack flag or migrate to multi-zones.

Zones cannot share React state directly because each zone runs its own React root. Shared state requires external storage like cookies, localStorage, or a backend API that all zones access.

Yes. Zones can route to any deployed application using rewrites. The remote application does not need to be a Next.js zone as long as it serves content at the routed path.

Failed zone deployments do not affect other zones. The host application continues serving its routes and other zones. Users navigating to the failed zone see errors only when accessing that zone's paths.

No. Each zone executes its own middleware independently. Shared middleware logic must be extracted to a package that zones import and execute separately.

Multi-zones replace runtime module federation with deployment-based architecture. The pattern aligns with Turbopack's build-time optimization model and removes the complexity of runtime module coordination. Organizations gain faster builds, independent zone deployments, and simpler dependency management at the cost of larger total JavaScript payloads.

The architectural shift requires rethinking micro-frontend boundaries. Zones work best when they represent distinct user workflows with infrequent navigation between them. Applications that require frequent component-level code sharing across teams may find the hard navigation overhead expensive. Those cases benefit from extracting shared code to packages rather than attempting runtime module sharing.

Turbopack's adoption as Next.js's default bundler makes multi-zones the standard approach for micro-frontends in the Next.js ecosystem. The webpack-based module federation pattern remains available through the --webpack flag but receives no optimization attention from the framework. Teams building new micro-frontend architectures should design around multi-zones from the start.

For further exploration of Next.js architectural patterns, see related posts on Next.js 15 caching changes, Next.js 16 form component progressive enhancement, and Next.js 16 instrumentation stable.

── more in #developer-tools 4 stories · sorted by recency
── more on @next.js 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/next-js-turbopack-mo…] indexed:0 read:16min 2026-10-30 · —