Setting up Module Federation with Server-Side Rendering

In this guide, we will walk you through setting up Module Federation with Server-Side Rendering (SSR) in a framework-agnostic manner. We assume you already have a basic understanding of Module Federation and have set it up in your project. If you need a refresher, please refer to the Module Federation setup guide. This guide focuses on the steps required to enable SSR with Module Federation. Table of Contents

  • Understanding the Challenges with SSR and Module Federation

  • Server-Side Bundle Configuration

  • Loading Remote Modules on the Server

  • Rendering Federated Modules on the Server

  • Hydrating the Client-Side Application

  • Conclusion

Understanding the Challenges with SSR and Module Federation

Server-Side Rendering (SSR) is a technique used to improve the initial load time and SEO of web applications by rendering the HTML on the server and sending it to the client. Module Federation, on the other hand, is a feature introduced in Webpack 5 that allows sharing code and dependencies across multiple applications at runtime.

While both techniques offer significant benefits, integrating them can be challenging. Module Federation relies on dynamic imports, which are not natively supported during server-side rendering. This guide will demonstrate how to overcome these challenges and set up Module Federation with SSR.

Server-Side Bundle Configuration

To enable SSR with Module Federation, you need to create a separate Webpack configuration for the server-side bundle. This bundle should include the ModuleFederationPlugin with the appropriate settings for server-side rendering.

Create a new webpack.server.config.js file with the following configuration:

const path = require('path');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');

module.exports = {
  target: 'node',
  mode: 'production',
  entry: './src/server.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'server.js',
    libraryTarget: 'commonjs2',
  },
  plugins: [
    new ModuleFederationPlugin({
      name: 'serverApp',
      library: { type: 'commonjs2' },
      filename: 'remoteServerEntry.js',
      exposes: {
        './serverComponent': './src/serverComponent',
      },
      shared: ['react', 'react-dom'],
    }),
  ],
};

This configuration specifies the target as node and sets the libraryTarget to commonjs2 since we are targeting a Node.js environment. Make sure to expose the relevant server components in the exposes object.

Loading Remote Modules on the Server

With the server-side bundle configuration in place, the next step is to load the remote modules on the server. We’ll use the require function to import the remote entry file generated by Webpack.

In your src/server.js file, add the following code to load the remote entry:

const path = require('path');
const { ModuleFederationPlugin } = require('webpack/lib/container/ModuleFederationPlugin');

const remoteEntryPath = path.resolve(__dirname, 'dist/remoteServerEntry.js');

require(remoteEntryPath);

// Your server-side rendering logic goes here

Rendering Federated Modules on the Server

Now that we have loaded the remote modules, we need to render them on the server. Since this guide is framework-agnostic, we will provide a general approach that you can adapt to your specific framework.

  1. Import the federated module in your server-side rendering code:

    const serverComponent = require('serverApp/serverComponent');
  2. Render the imported federated module to HTML:

    This step will depend on your specific framework. Most frameworks provide a method to render components to an HTML string on the server. Use that method to render the imported federated module.

    Here’s an example of how you might render the federated module using a hypothetical renderToString function:

    const renderedHTML = renderToString(serverComponent);
  3. Send the rendered HTML to the client:

    Once you have the rendered HTML, send it as part of the response to the client. This step will also depend on your specific framework and server setup.

    Here’s an example using Express.js:

    app.get('/', (req, res) => {
      res.send(`
        <!DOCTYPE html>
        <html>
          <head>
            <title>SSR with Module Federation</title>
          </head>
          <body>
            <div id="root">${renderedHTML}</div>
            <script src="/remoteEntry.js"></script>
            <script src="/client.js"></script>
          </body>
        </html>
      `);
    });

    In this example, we’ve included the remoteEntry.js and client.js files to load the federated modules on the client side.

Hydrating the Client-Side Application

With the server-side rendering complete, the final step is to hydrate the client-side application. Hydration is the process of attaching event listeners and initializing the state of the client-side application based on the server-rendered HTML.

  1. Load the remote entry file in your client-side index.html:

    <script src="/remoteEntry.js"></script>
  2. Import the federated module in your client-side application:

    import('./bootstrap.js');
  3. Hydrate the client-side application:

    This step will depend on your specific framework. Most frameworks provide a method to hydrate a client-side application based on the server-rendered HTML. Use that method to hydrate your application with the imported federated module.

    Here’s an example of how you might hydrate the client-side application using a hypothetical hydrate function:

    import { hydrate } from 'your-framework';
    import ClientComponent from './ClientComponent';
    
    hydrate(<ClientComponent />, document.getElementById('root'));

Conclusion

In this guide, we’ve shown you how to set up Module Federation with Server-Side Rendering in a framework-agnostic manner. By following these steps, you can enjoy the benefits of both Module Federation and SSR, enabling a better user experience, improved initial load times, and enhanced SEO.

Remember that the exact implementation will depend on your specific framework and server setup. Always consult the framework’s documentation for detailed guidance and best practices.

For non-framework-agnostic guides, please refer to the following resources:

  • React: Module Federation with Server-Side Rendering

  • Vue.js: Module Federation with Server-Side Rendering

  • Angular: Module Federation with Server-Side Rendering

  • Svelte: Module Federation with Server-Side Rendering

These guides will provide you with specific instructions and examples tailored to each respective framework.