Comprehensive Guide to Integrating Module Applications with NextPederationPlugin

0

1. Overview of Flight and Hotel Modules

The Flight and Hotel modules are independent applications that provide flight time booking and hotel booking functionalities, respectively. These two modules are integrated into the app-front application using NextPederationPlugin. This article will walk you through the configuration and setup of each module step-by-step.

2. Configuring the Flight Module

1. `package.json` and Main Scripts

The `package.json` file is configured as follows:

{
  "name": "amu-front-flight",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "rm -rf .next && SET NEXT_PRIVATE_LOCAL_WEBPACK=true && next dev -p 3011",
    "build": "SET NEXT_PRIVATE_LOCAL_WEBPACK=true && next build"
  },
  "dependencies": {
    "next": "^12.0.7",
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  },
  "devDependencies": {
    "typescript": "^4.5.2",
    "webpack": "^5.58.2"
  }
}
  • dev: Runs the development server, setting the `NEXT_PRIVATE_LOCAL_WEBPACK` environment variable to execute Webpack locally.
  • build: Build command that sets the `NEXT_PRIVATE_LOCAL_WEBPACK` environment variable to perform the build.

2. Configuring the `next.config.js` File and Using NextFederationPlugin

The `next.config.js` file is configured as follows:

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

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  compiler: {
    styledComponents: true,
  },
  eslint: {
    ignoreDuringBuilds: true,
  },
  webpack(config, options) {
    config.resolve.alias.canvas = false;
    const url = 'http://localhost:3011';
    const location = options.isServer ? 'ssr' : 'chunks';
    config.plugins.push(
      new NextFederationPlugin({
        name: 'flight',
        filename: 'static/chunks/remoteEntry.js',
        remotes: {
          container: `container@${url}/_next/static/${location}/remoteEntry.js`,
        },
        exposes: {
          './home': './src/pages/flight/index.tsx',
          './search': './src/pages/flight/search/index.tsx',
          './flightSearch': './src/components/FlightSearch/index.tsx',
        },
        shared: {
          'styled-components': { eager: true, singleton: true },
          '@tanstack/react-query': { eager: true, singleton: true },
          dayjs: { eager: true, singleton: true },
          '@amu/amu-ui-system': { eager: true, singleton: true },
          'next/image': { eager: true, singleton: true },
        },
      })
    );

    return config;
  },
  images: {
    domains: ['*', 'cdn.domain.net'],
    formats: ['image/webp'],
    dangerouslyAllowSVG: true,
  },
};

module.exports = nextConfig;

NextFederationPlugin is configured for module integration.

  • name: The name of the current module.
  • filename: The name of the remote entry file.
  • remotes: URL settings for remote modules.
  • exposes: The paths of the components provided by the current module.
  • shared: Settings for shared modules.

3. File and Folder Structure of the Flight Module

/flight
  ├── .next
  ├── node_modules
  ├── public
  └── src
      ├── components
      │   └── FlightSearch
      │       └── index.tsx
      ├── pages
      │   ├── flight
      │   │   ├── search
      │   │   │   └── index.tsx
      │   │   └── index.tsx
      │   ├── _app.tsx
      │   └── _document.tsx
      ├── styles
      │   └── globalStyle.ts
      ├── types
      │   └── common.ts
      ├── utils
      │   └── server.ts
      └── remoteType.d.ts
  ├── env.development
  ├── next.config.js
  ├── package.json
  └── tsconfig.json

4. Setting Up the `FlightSearch` Component and Server-Side Rendering

// src/components/FlightSearch/index.tsx
import React from 'react';

const FlightSearch = () => {
  return <div>Flight Search Component</div>;
};

export default FlightSearch;
// src/pages/flight/index.tsx
import Head from 'next/head';
import { Button } from '@amu/amu-ui-system';
import { useRouter } from 'next/router';
import { PageProps } from 'types/common';
import { makeGetServerSideProps } from 'utils/server';
import FlightSearch from 'components/FlightSearch';
import styled from 'styled-components';

export default function Home({ isMobile }: PageProps) {
  const router = useRouter();

  return (
    <>
      <Head>
        <title>Flight Home</title>
      </Head>
      <ButtonWrapper>
        <Button>Search Flights</Button>
      </ButtonWrapper>
      <FlightSearch />
    </>
  );
}

const ButtonWrapper = styled.div`
  margin-bottom: 20px;
`;

export const getServerSideProps = makeGetServerSideProps();

3. Configuring the Hotel Module

1. `package.json` and Main Scripts

The `package.json` file is configured similarly to the `flight` module but with different `name` and port number:

{
  "name": "amu-front-hotel",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "rm -rf .next && SET NEXT_PRIVATE_LOCAL_WEBPACK=true && next dev -p 3012",
    "build": "SET NEXT_PRIVATE_LOCAL_WEBPACK=true && next build"
  },
  "dependencies": {
    "next": "^12.0.7",
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  },
  "devDependencies": {
    "typescript": "^4.5.2",
    "webpack": "^5.58.2"
  }
}

2. Configuring the `next.config.js` File

The `next.config.js` file is almost identical to the `flight` module, with the main difference being the `exposes` section:

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

const nextConfig = {
  reactStrictMode: true,
  compiler: {
    styledComponents: true,
  },
  eslint: {
    ignoreDuringBuilds: true,
  },
  webpack(config, options) {
    config.resolve.alias.canvas = false;
    const url = 'http://localhost:3012';
    const location = options.isServer ? 'ssr' : 'chunks';
    config.plugins.push(
      new NextFederationPlugin({
        name: 'hotel',
        filename: 'static/chunks/remoteEntry.js',
        remotes: {
          container: `container@${url}/_next/static/${location}/remoteEntry.js`,
        },
        exposes: {
          './home': './src/pages/hotel/index.tsx',
          './search': './src/pages/hotel/search/index.tsx',
          './hotelSearch': './src/components/HotelSearch/index.tsx',
        },
        shared: {
          'styled-components': { eager: true, singleton: true },
          '@tanstack/react-query': { eager: true, singleton: true },
          dayjs: { eager: true, singleton: true },
          '@amu/amu-ui-system': { eager: true, singleton: true },
          'next/image': { eager: true, singleton: true },
        },
      })
    );

    return config;
  },
  images: {
    domains: ['*', 'cdn.domain.net'],
    formats: ['image/webp'],
    dangerouslyAllowSVG: true,
  },
};

module.exports = nextConfig;

3. File and Folder Structure of the Hotel Module

The structure is similar to the `flight` module.

/hotel
  ├── .next
  ├── node_modules
  ├── public
  └── src
      ├── components
      │   └── HotelSearch
      │       └── index.tsx
      ├── pages
      │   ├── hotel
      │   │   ├── search
      │   │   │   └── index.tsx
      │   │   └── index.tsx
      │   ├── _app.tsx
      │   └── _document.tsx
      ├── styles
      │   └── globalStyle.ts
      ├── types
      │   └── common.ts
      ├── utils
      │   └── server.ts
      └── remoteType.d.ts
  ├── env.development
  ├── next.config.js
  ├── package.json
  └── tsconfig.json

4. Setting Up the `HotelSearch` Component and Server-Side Rendering

// src/components/HotelSearch/index.tsx
import React from 'react';

const HotelSearch = () => {
  return <div>Hotel Search Component</div>;
};

export default HotelSearch;
// src/pages/hotel/index.tsx
import Head from 'next/head';
import { Button } from '@amu/amu-ui-system';
import { useRouter } from 'next/router';
import { PageProps } from 'types/common';
import { makeGetServerSideProps } from 'utils/server';
import HotelSearch from 'components/HotelSearch';
import styled from 'styled-components';

export default function Home({ isMobile }: PageProps) {
  const router = useRouter();

  return (
    <>
      <Head>
        <title>Hotel Home</title>
      </Head>
      <ButtonWrapper>
        <Button>Search Hotels</Button>
      </ButtonWrapper>
      <HotelSearch />
    </>
  );
}

const ButtonWrapper = styled.div`
  margin-bottom: 20px;
`;

export const getServerSideProps = makeGetServerSideProps();

5. Handling Mobile Devices

When rendering on the server-side, you can detect mobile devices and provide the appropriate UI. Define the following function in the `server.ts` file:

// src/utils/server.ts
import { GetServerSidePropsContext } from 'next';

export const checkMobileDevice = (context: GetServerSidePropsContext) => {
  return Boolean(context?.req?.headers['user-agent']?.includes('Mobi'));
};

export const makeGetServerSideProps = () => (context: GetServerSidePropsContext) => {
  const isMobile = checkMobileDevice(context);

  return {
    props: {
      isMobile,
    },
  };
};

This function uses `getServerSideProps` to detect whether the request is from a mobile device and passes that information to the component.

Conclusion

In this article, we covered how to configure and set up the Flight and Hotel module applications. Each module is developed independently and integrated using NextPederationPlugin. In the next article, we will discuss how to configure these two applications along with the previously created UI system package into the app-front application, and integrate these modules. This will allow us to build a final, fully integrated application.

Leave a Reply