Rsbuild, error loading module - Cannot read properties of null (reading 'useState')

When I try to load a microfrontend using Rsbuild with the module federation plugin and React version 19.0.0, the following error occurs:

React TypeError: Cannot read properties of null (reading 'useState')

Solution

Probably, the issue arises because two separate instances of react are being loaded—one by the host application and another by the microfrontend. This conflict can be resolved by updating the Rsbuild module federation configuration.

To fix this, configure react and react-dom as shared dependencies and set the singleton property to true. This ensures that React is only loaded once across the host and microfrontend.

Below is an example configuration for the rsbuild.config.ts file:

import { pluginModuleFederation } from '@module-federation/rsbuild-plugin';
import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';

export default defineConfig({
  plugins: [
    pluginReact(),
    pluginModuleFederation({
      name: 'app_name',
      remotes: {
        //remote config here
      },
      shared: {
        react: {
          singleton: true,
        },
        'react-dom': {
          singleton: true,
        },
      },
    }),
  ],
});

When you enable the singleton mode, shared dependencies between the host application and remote modules are loaded only once. If multiple versions of a dependency are detected, the highest version is used, and a warning is issued for the lower version.

For more details, refer to the documentation for the rspack module-federation-plugin.

singleton: Ensure that shared modules are only loaded once between different versions, following the singleton pattern. This is necessary for libraries designed to run as singletons, such as React, as it can prevent various issues caused by instantiating multiple library instances.

Alternative #1

I've encountered this exact issue in production with Rsbuild and module federation. Sometimes the singleton configuration isn't enough, especially if you have different React versions between host and remote apps.

You can also try setting the requiredVersion to ensure compatibility:

import { pluginModuleFederation } from '@module-federation/rsbuild-plugin';
import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';

export default defineConfig({
  plugins: [
    pluginReact(),
    pluginModuleFederation({
      name: 'app_name',
      shared: {
        react: {
          singleton: true,
          requiredVersion: '^19.0.0', // Specify exact version
        },
        'react-dom': {
          singleton: true,
          requiredVersion: '^19.0.0',
        },
      },
    }),
  ],
});

This forces all apps to use the same React version, preventing the null reference issue.

Alternative #2

Another common cause I've seen is import order or bundling issues. You can try adding eager: true to force the shared dependencies to be loaded immediately:

import { pluginModuleFederation } from '@module-federation/rsbuild-plugin';
import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';

export default defineConfig({
  plugins: [
    pluginReact(),
    pluginModuleFederation({
      name: 'app_name',
      shared: {
        react: {
          singleton: true,
          eager: true, // Force immediate loading
        },
        'react-dom': {
          singleton: true,
          eager: true,
        },
      },
    }),
  ],
});

This ensures React is available before any remote modules try to use it.

Alternative #3

If you're still having issues, you might need to exclude React from the remote bundle entirely and rely on the host's React instance:

import { pluginModuleFederation } from '@module-federation/rsbuild-plugin';
import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';

export default defineConfig({
  plugins: [
    pluginReact(),
    pluginModuleFederation({
      name: 'app_name',
      exposes: {
        './MyComponent': './src/components/MyComponent',
      },
      shared: {
        react: {
          singleton: true,
          eager: true,
        },
        'react-dom': {
          singleton: true,
          eager: true,
        },
      },
      // Exclude React from remote bundle
      externals: {
        react: 'react',
        'react-dom': 'react-dom',
      },
    }),
  ],
});

This approach ensures only one React instance exists across your entire microfrontend architecture.

Last modified: April 30, 2025