@module-federation/vite

repository·main·Indexed 21 days ago

https://github.com/module-federation/vite

A Vite plugin that enables Module Federation, allowing developers to load separately compiled and deployed code into a single application to facilitate micro-frontend architectures. It supports host and remote application configurations, shared dependency tree shaking, runtime instance creation, and dynamic remote registration via methods like registerRemotes() and loadRemote().

Tokens
10.2K
Snippets
22
Records
40
Agent score
74%

What's inside @module-federation/vite

  1. Use an External Runtime via experiments

    main

    To avoid remotes bundling their own copy of @module-federation/runtime-core, you can share a single instance from a pure consumer host. This is achieved using the experiments configuration.

    Implementation Pattern

    1. Host (Pure Consumer): Must set experiments.provideExternalRuntime: true. This publishes the runtime to globalThis._FEDERATION_RUNTIME_CORE. Note: A host can only provide the runtime if it does not use exposes.
    2. Remote: Must set experiments.externalRuntime: true. This rewrites imports of @module-federation/runtime-core to use the global instance.

    Note: externalRuntime applies to the browser remote graph. SSR remotes will continue to resolve the runtime from Node.

    // Host (pure consumer, no 'exposes')
    federation({
      name: "host",
      remotes: {
        remote: {
          type: "module",
          name: "remote",
          entry: "http://localhost:5176/remoteEntry.js",
        },
      },
      experiments: {
        provideExternalRuntime: true,
      },
    });
    
    // Remote
    federation({
      name: "remote",
      filename: "remoteEntry.js",
      exposes: {
        "./App": "./src/App.tsx",
      },
      experiments: {
        externalRuntime: true,
      },
    });
  2. Runtime Host Capabilities in Module Federation

    main

    In a pure runtime host setup, the host manages the Module Federation lifecycle dynamically without needing a static configuration for every remote. Key capabilities demonstrated in this pattern include:

    1. Runtime Instance Creation: The host initializes a Module Federation runtime instance.
    2. Dynamic Dependency Registration: The host can register lazy, get-only shared dependencies (e.g., React) at runtime.
    3. Remote Registration: Remotes are added to the runtime using the registerRemotes() method.
    4. Module Loading: Exposed modules from registered remotes are fetched and loaded using the loadRemote() method.
  3. Run the Vite Runtime Register Example

    main

    This example demonstrates a pure runtime host interacting with a Vite remote. The host creates a Module Federation runtime instance, registers lazy React shared dependencies, registers remotes using registerRemotes(), and loads exposed modules using loadRemote().

    To run the development servers, use the following commands:

    pnpm --filter examples-vite-runtime-register-remote dev
    pnpm --filter examples-vite-runtime-register-host dev

    Access URLs:

    • Host: http://localhost:4175
    • Remote: http://localhost:4176

    To run the preview builds, use:

    pnpm --filter examples-vite-runtime-register-remote preview
    pnpm --filter examples-vite-runtime-register-host preview
  4. Configure a Host Application

    main

    A Host application consumes remotes. In your vite.config.ts, use the federation plugin to define remotes (mapping a name to an entry URL and type), shared dependencies, and other runtime behaviors like injection location and manifest generation.

    import { defineConfig } from 'vite';
    import { federation } from '@module-federation/vite';
    
    export default defineConfig({
      // ...
      plugins: [
        // ...
        federation({
          name: "host",
          remotes: {
            remote: {
              type: "module", // type "var" (default) for vite remote is supported with remote's `varFilename` option
              name: "remote",
              entry: "https://[...]/remoteEntry.js",
              entryGlobalName: "remote",
              shareScope: "default",
            },
          },
          filename: "remoteEntry.js",
          shared: ["vue"],
          hostInitInjectLocation: "html", // or "entry"
          bundleAllCSS: false, // or true
          moduleParseTimeout: 10,
          moduleParseIdleTimeout: 10,
          manifest: {
            fileName: "mf-manifest.json",
            filePath: "dist/",
            disableAssetsAnalyze: false,
            additionalData: ({ stats }) => {
              stats.metaData.deployEnv = process.env.NODE_ENV;
              stats.metaData.region = "eu";
              stats.custom = {
                buildId: process.env.BUILD_ID,
              };
            },
          },
        }),
      ],
      server: {
        origin: "http://localhost:{Your port}"
      },
      // ...
    });
  5. Migrate from @originjs/vite-plugin-federation to @module-federation/vite

    main

    This guide outlines the process of migrating hosts and remotes from the OriginJS plugin to @module-federation/vite.

    Key Differences

    • Remote Declaration: OriginJS defaults to esm for URL strings. @module-federation/vite uses a runtime model where string shorthands are treated as var remotes. Vite ESM remotes must be explicitly declared with type: 'module'.
    • File Paths: By default, @module-federation/vite places the remote entry at dist/remoteEntry.js, whereas OriginJS uses dist/assets/remoteEntry.js. To maintain the old path, set filename: 'assets/remoteEntry.js' in your configuration.
    • Compatibility: @module-federation/vite can consume Vite ESM remotes, var remotes (from Vite, Webpack, or Rspack), and manifests.

    Requirements

    Ensure your environment meets these minimum versions:

    • Node.js: ^20.19.0 or >=22.12.0
    • Vite: 5, 6, 7, or 8
    // OriginJS host
    remotes: {
      catalog: "https://cdn.example.com/catalog/remoteEntry.js",
    }
    
    // @module-federation/vite host
    remotes: {
      catalog: {
        name: "catalog",
        entry: "https://cdn.example.com/catalog/remoteEntry.js",
        type: "module",
      },
    }
    
    // Consumer code (unchanged in both)
    const Product = await import("catalog/Product");
  6. Publish packages to npm via GitHub Actions

    main

    Packages are published to npm using GitHub Actions trusted publishing (OIDC + provenance). The process relies on the version in package.json matching the GitHub Release tag exactly.

    Standard Release Flow

    1. Update Version: Update package.json to the target semver version and run pnpm install --lockfile-only if necessary.
    2. Create GitHub Release: Create a release for the merge commit. The tag must match the package.json version exactly (e.g., 1.11.0) and must not start with v.
    3. Automated Publish: The Publish (GitHub Release) workflow (.github/workflows/publish-on-release.yml) triggers automatically.

    Distribution Tags (dist-tag)

    • Stable releases: Published with the latest tag.
    • Pre-releases: Published with the next tag. The workflow automatically patches the version to <base>-next.<N> (e.g., 1.12.0-next.1) before publishing.

    Important Constraints

    • Tag Mismatch: The publish job will hard-fail if the GitHub tag_name does not match the package.json version.
    • No Dist-tag Promotion: Because the project uses Trusted Publishers (OIDC), you cannot promote a version from one dist-tag to another (e.g., using npm dist-tag add) without a full publish. If a version exists on npm but is under a different dist-tag than requested, the workflow will fail.
  7. Migrate shared dependency configuration from OriginJS

    main

    When migrating from @originjs/vite-plugin-federation to @module-federation/vite, do not copy the shared configuration mechanically. While many options are compatible, some require manual review or have no direct equivalent.

    Configuration Mapping

    OriginJS optionMigration action
    shared: ['react', 'react-dom']Supported unchanged. Configure singleton: true separately only when the host and remote must share the same instance.
    requiredVersion, shareScopeSupported by @module-federation/vite. Retain them after verifying configuration on both sides.
    versionString values are supported. Note that OriginJS's version: false has no direct equivalent; review these cases manually.
    import: falseSupported, but the remote has no local fallback. Ensure the host provides a compatible version in the same share scope.
    packagePathNo direct equivalent. Review package resolution manually.
    generate: falseNo direct equivalent. Do not assume the same fallback artifact behavior.
    modulePreloadNo direct equivalent. Review application build and loading behavior manually.
    dontAppendStylesToHeadNo direct equivalent. If using Shadow DOM, manage stylesheet URLs or styles explicitly and inject them into the ShadowRoot. bundleAllCSS is not a replacement.

    React Specifics

    If the host and remote use React within the same rendering boundary, you must:

    1. Verify compatible versions.
    2. Configure react and react-dom as singletons.
    3. Verify subpath imports that cross the boundary (e.g., react/jsx-runtime and react-dom/client).
  8. Setup and run the Rsbuild Project example

    main

    To set up and run the Rsbuild Project example, use pnpm to install dependencies and manage the development and production lifecycles.

    Installation

    Install all project dependencies using:

    pnpm install

    Development

    Start the local development server with:

    pnpm dev

    Production

    To prepare the application for production, build the assets:

    pnpm build

    To verify the production build locally before deployment, use the preview command:

    pnpm preview
    pnpm install
    pnpm dev
    pnpm build
    pnpm preview
  9. Migrate a Vite host

    main

    When migrating a host, you must change the remotes configuration from a simple URL string to an object specifying the type.

    Crucial: A string remote in @module-federation/vite is interpreted as a var remote. For Vite ESM remotes, you must use the object syntax with type: 'module'.

    To avoid introducing singleton policy issues during the initial migration, keep the shared configuration in its array form (e.g., shared: ['react']) rather than switching to object configuration immediately.

    import { defineConfig } from "vite";
    import { federation } from "@module-federation/vite";
    
    export default defineConfig({
      plugins: [
        federation({
          name: "storefront",
          remotes: {
            catalog: {
              name: "catalog",
              entry: "https://cdn.example.com/catalog/remoteEntry.js",
              type: "module",
            },
          },
          shared: ["react", "react-dom"],
        }),
      ],
    });
  10. Migrate a Vite-built remote

    main

    Replace the @originjs/vite-plugin-federation import with @module-federation/vite. The name and exposes keys remain unchanged, ensuring existing consumer imports continue to work.

    Note on filename: If you need to keep the existing /assets/remoteEntry.js URL used by OriginJS, you must set filename: 'assets/remoteEntry.js' in the federation configuration.

    import { defineConfig } from "vite";
    import { federation } from "@module-federation/vite";
    
    export default defineConfig({
      plugins: [
        federation({
          name: "catalog",
          filename: "remoteEntry.js", // Use 'assets/remoteEntry.js' to match OriginJS default
          exposes: {
            "./Product": "./src/Product.tsx",
          },
          shared: ["react", "react-dom"],
        }),
      ],
    });