Never Propel Marmalade

    @module-federation/nextjs-mf

    3.0.1 • Public • Published

    Module Federation For Next.js

    This plugin enables Module Federation on Next.js

    This is a stable and viable solution to leverage Module Federation until this issue is resolved.

    Supports

    • next ^10.2.x || ^11.x.x || ^12.x.x
    • Client side only

    Whats shared by default?

    Under the hood we share some next internals automatically You do not need to share these packages, sharing next internals yourself will cause errors.

    const sharedDefaults = {
      "next/dynamic": {
        requiredVersion: false,
        singleton: true,
      },
      "styled-jsx": {
        requiredVersion: false,
        singleton: true,
      },
      "next/link": {
        requiredVersion: false,
        singleton: true,
      },
      "next/router": {
        requiredVersion: false,
        singleton: true,
      },
      "next/script": {
        requiredVersion: false,
        singleton: true,
      },
      "next/head": {
        requiredVersion: false,
        singleton: true,
      },
    };

    Things to watch out for

    There's a bug in next.js which causes it to attempt and fail to resolve federated imports on files imported into the pages/index.js

    Its recommended using the low-level api to be safe.

    const SampleComponent = dynamic(
      () => window.next2.get("./sampleComponent").then((factory) => factory()),
      {
        ssr: false,
      }
    );

    Make sure you are using mini-css-extract-plugin@2 - version 2 supports resolving assets through publicPath:'auto'

    Options

    withFederatedSidecar(
      {
        name: "next2",
        filename: "static/chunks/remoteEntry.js",
        exposes: {
          "./sampleComponent": "./components/sampleComponent.js",
        },
        shared: {
          react: {
            // Notice shared are NOT eager here.
            requiredVersion: false,
            singleton: true,
          },
        },
      },
      {
        removePlugins: [
          // optional
          // these are the defaults
          "BuildManifestPlugin",
          "ReactLoadablePlugin",
          "DropClientPage",
          "WellKnownErrorsPlugin",
          "ModuleFederationPlugin",
        ],
        publicPath: "auto", // defaults to 'auto', is optional
      }
    );

    Demo

    You can see it in action here: https://github.com/module-federation/module-federation-examples/tree/master/nextjs

    How to add a sidecar for exposes to your nextjs app

    1. Use withFederatedSidecar in your next.config.js of the app that you wish to expose modules from. We'll call this "next2".
    // next.config.js
    const { withFederatedSidecar } = require("@module-federation/nextjs-mf");
    
    module.exports = withFederatedSidecar({
      name: "next2",
      filename: "static/chunks/remoteEntry.js",
      exposes: {
        "./sampleComponent": "./components/sampleComponent.js",
      },
      shared: {
        react: {
          // Notice shared are NOT eager here.
          requiredVersion: false,
          singleton: true,
        },
      },
    })({
      // your original next.config.js export
    });
    1. For the consuming application, we'll call it "next1", add an instance of the ModuleFederationPlugin to your webpack config, and ensure you have a custom Next.js App pages/_app.js (or .tsx):
    module.exports = {
      webpack(config, options) {
        config.plugins.push(
          new options.webpack.container.ModuleFederationPlugin({
            remoteType: "var",
            remotes: {
              next2: "next2",
            },
            shared: {
              react: {
                // Notice shared ARE eager here.
                eager: true,
                singleton: true,
                requiredVersion: false,
              },
              // we have to share something to ensure share scope is initialized
              "@module-federation/nextjs-mf/lib/noop": {
                eager: false,
              },
            },
          })
        );
    
        // we attach next internals to share scope at runtime
        config.module.rules.push({
          test: /pages\/_app.[jt]sx?/,
          loader: "@module-federation/nextjs-mf/lib/federation-loader.js",
        });
    
        return config;
      },
    };
    1. Add the remote entry for "next2" to the _document for "next1"
    import Document, { Html, Head, Main, NextScript } from "next/document";
    
    class MyDocument extends Document {
      static async getInitialProps(ctx) {
        const initialProps = await Document.getInitialProps(ctx);
        return { ...initialProps };
      }
    
      render() {
        return (
          <Html>
            <Head />
            <body>
              <Main />
              <script src="http://next2-domain-here.com/_next/static/chunks/remoteEntry.js" />
              <NextScript />
            </body>
          </Html>
        );
      }
    }
    
    export default MyDocument;
    1. Use next/dynamic to import from your remotes
    import dynamic from "next/dynamic";
    
    const SampleComponent = dynamic(
      () => window.next2.get("./sampleComponent").then((factory) => factory()),
      {
        ssr: false,
      }
    );

    Contact

    If you have any questions or need to report a bug Reach me on Twitter @ScriptedAlchemy

    Or join this discussion thread: https://github.com/module-federation/module-federation-examples/discussions/978

    Keywords

    none

    Install

    npm i @module-federation/nextjs-mf

    DownloadsWeekly Downloads

    941

    Version

    3.0.1

    License

    MIT

    Unpacked Size

    5.76 kB

    Total Files

    4

    Last publish

    Collaborators

    • alexg53090
    • zackljackson
    • marais
    • tysonrm