DEV Community

Cover image for Break Up Your React App: Micro-Frontends with Next.js 🧩
Prajapati Paresh
Prajapati Paresh

Posted on • Originally published at smarttechdevs.in

Break Up Your React App: Micro-Frontends with Next.js 🧩

The Frontend Monolith Bottleneck

Backend architectures evolved from monoliths to decoupled services years ago, but the frontend was largely left behind. As enterprise React applications grow, the main repository becomes a bottleneck. Deployment times skyrocket, merge conflicts become a daily nightmare, and a critical bug in the "Support Chat" feature can crash the "Checkout" flow.

At Smart Tech Devs, we scale massive UI ecosystems by adopting Micro-Frontends. This architectural pattern allows independent teams to build, test, and deploy their UI features separately, while appearing as a single cohesive application to the end user.

Module Federation in Next.js

The standard way to achieve micro-frontends today is through Webpack Module Federation. This allows a Next.js application to dynamically load code from another Next.js application at runtime.

Step 1: The Host and The Remote

In this architecture, you have a Host application (the main shell of your app, handling routing and global layout) and several Remote applications (e.g., a standalone Dashboard app, or a standalone Billing app).

You configure the Next.js Webpack settings to expose specific components from the remote, and consume them in the host.


// ✅ THE ENTERPRISE PATTERN: Remote App next.config.js
const NextFederationPlugin = require('@module-federation/nextjs-mf');

module.exports = {
  webpack(config, options) {
    config.plugins.push(
      new NextFederationPlugin({
        name: 'billing_app',
        filename: 'static/chunks/remoteEntry.js',
        exposes: {
          // Exposing a specific component to be used anywhere
          './InvoiceList': './components/InvoiceList.tsx',
        },
        shared: ['react', 'react-dom'],
      })
    );
    return config;
  },
};

Step 2: Dynamic Consumption

In your Host application, you can now import the InvoiceList as if it were a local component, but it will be fetched dynamically over the network from the Billing deployment.


import dynamic from 'next/dynamic';
import { Suspense } from 'react';

// Dynamically import the component from the remote billing app
const RemoteInvoiceList = dynamic(
  () => import('billing_app/InvoiceList'),
  { suspense: true, ssr: false }
);

export default function Dashboard() {
  return (
    <main className="p-8">
      <h1>Main Dashboard Shell</h1>
      
      <Suspense fallback={<p>Loading Billing Module...</p>}>
        <RemoteInvoiceList />
      </Suspense>
    </main>
  );
}

The Engineering ROI

By implementing Module Federation, your "Billing" team can deploy updates to the invoice UI five times a day without ever touching the core repository or requiring the "Dashboard" team to re-deploy. It maximizes team autonomy, isolated testing, and fault tolerance.

Top comments (0)