DEV Community

Cover image for Module Federation with SSR and React 18
Alexey Umanskiy for Valor Labs

Posted on • Updated on

Module Federation with SSR and React 18

Introduction

Web Development for medium to large-size projects is a tortuous road that often requires complex dependencies and components management. Module Federation is a technology solution that solves the challenges that come with large scale and creates a streamlined development process.

We're going to explore the combination of Server-Side-Rendering and Module Federation with React 18 and Webpack. The solutions described here are not limited to the listed technologies and can be reused in various combinations with other tools.

Multiple independent projects can work together as a single application using module federation. This makes it possible to design web applications more modularly, allowing several teams to work on various components of the program without having to be closely coupled. In the Module Federation, every project is a unique module that can consume and expose other modules.

By rendering a JavaScript application on the server, sending the finished HTML to the browser, and then adding JavaScript to the HTML to make the program interactive, SSR creates a web page. This technique typically improves JavaScript application performance, which is especially helpful for people with sluggish internet connections or outdated hardware.

We may use the same components and modules across various portions of the application and still keep the performance advantages of SSR when we combine Module Federation and SSR with React. This makes the development process more effective and enhances the user experience.

There are several potential difficulties to take into account while utilizing Module Federation with SSR and React. Making sure the modules are correctly exposed and consumed by the host and remote apps is one of the problems. Additionally, managing the application's state could be difficult, particularly if it needs to be displayed on the server before being hydrated on the client.

Using libraries like @module-federation/nextjs-mf, which offers a set of utilities to handle the configuration and setup of the host and remote applications, developers can get around these difficulties. The state of the application should be handled correctly on the server and client, and developers should make sure that the modules are exposed and consumed effectively.

Utilizing Module Federation with SSR and React can enhance the creation of online applications and the user experience, but it's vital to take into account the difficulties and use the proper tools and libraries to handle the setup and configuration of the host and remote applications.

Practical Sample

Step 1: Create the host and remote applications.

mkdir my-app
cd ./my-app
Enter fullscreen mode Exit fullscreen mode


npx create-next-app host
cd host
npm install --save @module-federation/nextjs-mf
cd ../
Enter fullscreen mode Exit fullscreen mode


npx create-react-app remote
cd remote
npm install --save-dev webpack webpack-cli html-webpack-plugin webpack-dev-server babel-loader
Enter fullscreen mode Exit fullscreen mode

Step 2: Create the webpack configuration for the remote application.


const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;
const path = require('path');
module.exports = {
    entry: './src/index',
    mode: 'development',
    devServer: {
        static: {
            directory: path.join(__dirname, 'dist'),
        },
        port: 3001,
    },
    output: {
        publicPath: 'http://localhost:3001/',
    },
    module: {
        rules: [
            {
                test: /\.jsx?$/,
                loader: 'babel-loader',
                exclude: /node_modules/,
                options: {
                    presets: ['@babel/preset-react'],
                },
            },
        ],
    },
    plugins: [
        new ModuleFederationPlugin({
            name: 'remote',
            library: { type: 'var', name: 'remote' },
            filename: 'remote.js',
            exposes: {
                './Nav': './src/Nav',
            },
            shared: {
                react: {
                    singleton: true,
                    version: '0',
                    requiredVersion: false,
                },
                'react-dom': {
                    requiredVersion: false,
                    singleton: true,
                    version: '0',
                },
            },
        }),
        new HtmlWebpackPlugin({
            template: './public/index.html',
        }),
    ],
};
Enter fullscreen mode Exit fullscreen mode

Step 3: Create the next.config.js file for the host application.

// host/next.config.js
const { NextFederationPlugin } = require('@module-federation/nextjs-mf');
module.exports = {
  webpack(config, options) {
    if (!options.isServer) {
      config.plugins.push(
          new NextFederationPlugin({
            name: 'host',
            remotes: {
              remote: 'remote@http://localhost:3001/remote.js',
            },
            filename: 'static/chunks/remoteEntry.js',
          }),
      );
    }
    return config;
  },
};
Enter fullscreen mode Exit fullscreen mode

Step 4: Create a remote component.

//remote/src/Nav.js
import React from 'react';
const Nav = () => {
    return (
        <div>
            This is my remote nav
            <nav>
                <a href="#">Home</a>
                <a href="#">About</a>
                <a href="#">Contact</a>
            </nav>
        </div>
    )
}
export default Nav;
Enter fullscreen mode Exit fullscreen mode

Step 5: Create an entry point for the remote app

//remote/index.js
import('./bootstrap');
//remote/bootstrap.js
import React from 'react';
import App from './App';
import { createRoot } from 'react-dom/client';
const container = document.getElementById('root');
const root = createRoot(container);
root.render(<App />);
//remote/App.js
import React from 'react';
import Nav from './Nav';
function App() {
 return (
     <div className="App">
       <header className="App-header">
         <Nav />
       </header>
     </div>
 );
}
export default App;
Enter fullscreen mode Exit fullscreen mode



Step 6: Start the host application

npm run build
npm run start
Enter fullscreen mode Exit fullscreen mode

Step 7:Edit remote package.json

. . .
"scripts": {
 "start": "webpack-dev-server --config webpack.config.js",
 "build": "webpack --mode production",
 "clean": "rm -rf dist"
},
. . .
Enter fullscreen mode Exit fullscreen mode

Step 8: Build and start the remote application

npm run start
Enter fullscreen mode Exit fullscreen mode

Step 9: Import the remote component using next/dynamic

// host/pages/index.js
import dynamic from 'next/dynamic';
const Nav = dynamic(() => import('remote/Nav'), { ssr: false });
export default function HomePage() {
  return (
      <div>
        This is my ssr host
        <Nav />
      </div>
  );
}
Enter fullscreen mode Exit fullscreen mode


With these steps, we have set up a basic application that uses Module Federation with SSR and React 18. The above code samples can be modified to match your specific use case. With Module Federation, it's easy to share and reuse components across different parts of the application, making the development process more efficient.

This project is available here https://github.com/lexasq/ssr-react-18

About Valor Software

Official Module Federation(MF) partner, Valor is actively contributing to the MF ecosystem and unlocking new possibilities.

Valor is also providing enterprise support, consulting, and team augmentation email us at sales@valor-software.com to learn how we can help.

Top comments (2)

Collapse
 
jeromediver profile image
Jérôme Lanteri • Edited

wait... "Module Federation is a technology solution that solves the challenges that come with large scale and creates a streamlined development process." is this your only part where you expect to explain what "Federation" is ? Are you serious ? So "Federation" is an other one layer of something and we have to trust you with that: it is more simple with this other one layer of something in the jungle of layers dependencies ? Really ? Seriously... are you on the way to pretend to promote "Federation" by this way ? There is nothing serious there that can convince me to use "Federation". I just not understand what you said too. Can you seriously consider to take time to explain a bit more about what "Federation" is before to pretend anything else ? And, you are talking about scaling process... scaling how much ? how big can it be interesting to use it and why ?

Collapse
 
lexasq profile image
Alexey Umanskiy • Edited

Hi, thanks for pointing this out, initial goal of this article wasn't about understanding basics of Module Federation, it's to help people who want to use it for this particular usecase It assumes you already familiar with the tool to some extent. Introduction section just meant to tell you what we are talking here about. But I'll be glad to help you with your questions.

  1. Module Federation is an approach that you could use for your micro-frontend in order to bring it to runtime dependencies. Your app would act as a set of containers(remotes) and consumers(hosts) and while they would still be entangled inside, on the outside each of them would be hosted and build separately, which does reduce CI/CD time, how greatly, depends on your current process, but the idea is that you don't need to rebuild/retest the whole app on each update

  2. Scaling to a few mils of code. Whenever you hit a certain amount of code even your build time becomes not as comfortable, not talking about other CI operations. While you have some other tools that might help you to orchestrate testing and linting a project to only affected parts, like NX, this wouldn't help you with your build time, since your dependencies are also build time. If you take MF approach you'll be able to also build only affected parts of your application, and all consumers would get the changes automatically in runtime.

Basically just imagine that you could change wiring in your walls without ripping it out of the wall first, that's convenient, efficient and fast. I hope that answers your question and ease your concerns.