Git Product home page Git Product logo

nextjs-mf's Introduction

Module Federation For Next.js

This plugin enables Module Federation on Next.js

Supports

  • next ^11.x.x || ^12.x.x
  • Client side only, SSR has a PR open. Help needed

I highly recommend referencing this application which takes advantage of the best capabilities: https://github.com/module-federation/module-federation-examples

Looking for SSR support?

SSR support for federated applications is much harder, as such - it utilizes a different licensing model. If you need SSR support, consider this package instead - it does everything that nextjs-mf does, and them some. https://app.privjs.com/buy/packageDetail?pkg=@module-federation/nextjs-ssr

There is a pull request moving SSR into this repo and package - but it is not ready yet.

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 DEFAULT_SHARE_SCOPE = {
  react: {
    singleton: true,
    requiredVersion: false,
  },
  'react/': {
    singleton: true,
    requiredVersion: false,
  },
  'react-dom': {
    singleton: true,
    requiredVersion: false,
  },
  '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,
  },
};

Usage

const SampleComponent = dynamic(() => import('next2/sampleComponent'), {
  ssr: false,
});

If you want support for sync imports. It is possible in next@12 as long as there is an async boundary.

With async boundary installed at the page level. You can then do the following

if (process.browser) {
  const SomeHook = require('next2/someHook');
}
// if client only file
import SomeComponent from 'next2/someComponent';

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

Demo

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

Usage

const SampleComponent = dynamic(() => import('next2/sampleComponent'), {
  ssr: false,
});

If you want support for sync imports. It is possible in next@12 as long as there is an async boundary.

With async boundary installed at the page level. You can then do the following

if (process.browser) {
  const SomeHook = require('next2/someHook');
}
// if client only file
import SomeComponent from 'next2/someComponent';

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

Options

This plugin works exactly like ModuleFederationPlugin, use it as you'd normally. Note that we already share react and next stuff for you automatically.

Also NextFederationPlugin has own optional argument extraOptions where you can unlock additional features of this plugin:

new NextFederationPlugin({
  name: ...,
  filename: ...,
  remotes: ...,
  exposes: ...,
  shared: ...,
  extraOptions: {
    exposePages: true, // `false` by default
    enableImageLoaderFix: true, // `false` by default
    enableUrlLoaderFix: true, // `false` by default
  },
});
  • exposePages – exposes automatically all nextjs pages for you and theirs ./pages-map.
  • enableImageLoaderFix – adds public hostname to all assets bundled by nextjs-image-loader. So if you serve remoteEntry from http://example.com then all bundled assets will get this hostname in runtime. It's something like Base URL in HTML but for federated modules.
  • enableUrlLoaderFix – adds public hostname to all assets bundled by url-loader.

Demo

You can see it in action here: module-federation/module-federation-examples#2147

Implementing the Plugin

  1. Use NextFederationPlugin in your next.config.js of the app that you wish to expose modules from. We'll call this "next2".
// next.config.js
const NextFederationPlugin = require('@module-federation/nextjs-mf/NextFederationPlugin');

module.exports = {
  webpack(config, options) {
    if (!options.isServer) {
      config.plugins.push(
        new NextFederationPlugin({
          name: 'next2',
          remotes: {
            next1: `next1@http://localhost:3001/_next/static/chunks/remoteEntry.js`,
          },
          filename: 'static/chunks/remoteEntry.js',
          exposes: {
            './title': './components/exposedTitle.js',
            './checkout': './pages/checkout',
            './pages-map': './pages-map.js',
          },
          shared: {
            // whatever else
          },
        })
      );
    }

    return config;
  },
};

// _app.js or some other file in as high up in the app (like next's new layouts)
// this ensures various parts of next.js are imported and "used" somewhere so that they wont be tree shaken out
import '@module-federation/nextjs-mf/lib/include-defaults';
  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): Inside that _app.js or layout.js file, ensure you import include-defaults file
// next.config.js

const NextFederationPlugin = require('@module-federation/nextjs-mf/NextFederationPlugin');

module.exports = {
  webpack(config, options) {
    if (!options.isServer) {
      config.plugins.push(
        new NextFederationPlugin({
          name: 'next1',
          remotes: {
            next2: `next2@http://localhost:3000/_next/static/chunks/remoteEntry.js`,
          },
        })
      );
    }

    return config;
  },
};

// _app.js or some other file in as high up in the app (like next's new layouts)
// this ensures various parts of next.js are imported and "used" somewhere so that they wont be tree shaken out
import '@module-federation/nextjs-mf/lib/include-defaults';
  1. Use next/dynamic or low level api to import remotes.
import dynamic from 'next/dynamic';

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

// or

const SampleComponent = dynamic(() => import('next2/sampleComponent'), {
  ssr: false,
});

Utilities

Ive added a util for dynamic chunk loading, in the event you need to load remote containers dynamically.

import { injectScript } from '@module-federation/nextjs-mf/lib/utils';
// if i have remotes in my federation plugin, i can pass the name of the remote
injectScript('home').then((remoteContainer) => {
  remoteContainer.get('./exposedModule');
});
// if i want to load a custom remote not known at build time.

injectScript({
  global: 'home',
  url: 'http://somthing.com/remoteEntry.js',
}).then((remoteContainer) => {
  remoteContainer.get('./exposedModule');
});

Contact

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

Or join this discussion thread: module-federation/module-federation-examples#978

nextjs-mf's People

Contributors

bartjanvanassen avatar crutch12 avatar drphelps avatar elado avatar jacob-ebey avatar jherr avatar maximilianklein avatar moogus avatar nodkz avatar noreiller avatar ramsunvtech avatar renovate[bot] avatar scriptedalchemy avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.