How to Share Components Across Tech Stacks with Micro‑Frontend and Module Federation

This article explores engineering solutions for sharing UI components across different front‑end frameworks and repositories, detailing micro‑frontend approaches with Garfish, component‑level embedding, data synchronization, and alternative strategies like Webpack Module Federation and Bit, while comparing their advantages, limitations, and practical implementation steps.

ELab Team
ELab Team
ELab Team
How to Share Components Across Tech Stacks with Micro‑Frontend and Module Federation

Preface

As front‑end applications become increasingly complex, large monolithic apps and legacy back‑office systems accumulate technical debt. Micro‑frontend engineering offers a bridge to resolve heavy dependencies and migration burdens, enabling component‑level integration across tech stacks.

Business Background

In a typical scenario a modal rendered by a complex React component must be displayed inside a Vue‑based page (React‑in‑Vue). The existing back‑office is Vue, while new business requires migration to React, creating cross‑stack component reuse challenges.

Engineering Solution for Micro‑Component Sharing

Project Overview

Most back‑office projects need decoupling for independent team maintenance and better structure. The focus is on sharing components and code across repositories and tech stacks.

Our monorepo setup includes a legacy Vue repo and two new React repos. The goal is to reuse business components across these repos without modifying Vue code.

We expose reusable components as a micro‑component sub‑application (a child app) that is loaded once and renders any component via portals.

Using a Child App to Reuse Micro‑Components

Example code for loading a child app in Vue:

<template>
  <div id="id"></div>
</template>
<script lang="ts">
import { defineComponent } from '@vue/composition-api';
let id = 9999;
export default defineComponent({
  props: [],
  async mounted() {
    const app = await Garfish.loadApp('xxx', { domGetter: () => document.getElementById(this.id) });
    await app.mount();
  },
  beforeDestroy() {
    console.info(this.microComponentKey, '微前端组件卸载');
  }
});
</script>

To avoid multiple loads and performance issues, we design a singleton micro‑component sub‑app that renders components via portals and manages props changes.

type Meta = {
  domId: string;
  componentKey: string; // identifies which component to render
  props?: Record<any, any>;
  [_key_: string]: any; // events and other attributes
};

React rendering logic using the meta data:

portalRender.map(meta => {
  const { domId, componentKey, props: _props, ...rest } = meta;
  const container = document.getElementById(domId);
  if (!container) return null;
  return ReactDOM.createPortal(
    <Suspense fallback={null}>
      <Portals _componentKey_={componentKey} {...{ domId, ..._props, ...rest }} />
    </Suspense>,
    container,
    domId
  );
});

The Vue wrapper component loads the child app once and passes props via a manager:

<template>
  <div id="id"></div>
</template>
<script>
import { microComponentManager } from '../src/MicroComponentManager';
let id = 0;
export default {
  data() { return { id: `${++id}`, beforeDestroy: undefined }; },
  props: { props: { default: () => ({}) }, componentKey: { type: String, required: true }, subAppName: { type: String, required: true, default: '' } },
  async mounted() {
    const { unMount, error } = await MicroComponent.loadComponent();
    this.beforeDestroy = unMount;
  },
  beforeDestroy() { this.beforeDestroy && this.beforeDestroy({ domId: this.id, type: 'remove' }); }
};
</script>

The manager ensures only one child app instance is loaded, tracks reference counts, and synchronizes prop changes via Garfish channels.

class MicroComponent {
  private _loaded = false;
  private _app: any;
  private _count = 0;
  async loadComponent() {
    this._count++;
    if (!this._loaded) {
      this._loaded = true;
      this._app = await window.Garfish.loadApp(this._subAppName, { domGetter: () => document.body, props: { subAppName: this._subAppName } });
      await this._app.mount();
    }
    const unMount = (params) => { /* emit props change, decrement count, unmount when zero */ };
    return { unMount };
  }
  emit(event, params) { window.Garfish.channel.emit(genEventKey(this._subAppName, event), params); }
}

Summary of the Approach

By loading a single micro‑component sub‑application on the host page, all business components are rendered via portals to specified DOM nodes, achieving cross‑repo and cross‑stack reuse with minimal overhead.

Advantages

Simple, lightweight implementation; framework‑agnostic reuse.

Supports React‑in‑Vue and Vue‑in‑React scenarios.

Only one sub‑app is loaded regardless of component count, reducing runtime cost.

Can be integrated into any Garfish‑based application for team‑level sharing.

Single deployment ensures all consumers get the latest version.

Enables building a shared component sub‑app across repositories.

Disadvantages

No built‑in version management for shared components.

Requires a Garfish environment.

Introduces an extra sub‑project compared to simple shared component libraries.

Potential issues with keep‑alive scenarios.

Dependency management (React, UI libraries) can be cumbersome.

Runtime Component Marketplace

Beyond custom micro‑components, a component marketplace can provide upload/download, versioning, and remote loading capabilities. Garfish’s loadComponent API can load remote UMD bundles, while other ByteDance micro‑frontend frameworks offer federation‑like APIs.

Shared Code Solutions

Module Federation

Module Federation allows multiple independent builds to form a single application, sharing dependencies at runtime. It defines hosts (consumers) and remotes (providers). The host loads remote modules via a generated script that registers a global container with get and init methods.

// host webpack config
new ModuleFederationPlugin({
  name: "app1",
  remotes: { app2: "app2@http://localhost:3002" },
  shared: { react: { singleton: true }, "react-dom": { singleton: true } }
});

Remote’s remoteEntry.js exposes components (e.g., ./Button) and shares React as a singleton, enabling the host to reuse the remote component without duplicate React instances.

// remote webpack config
new ModuleFederationPlugin({
  name: "app2",
  library: { type: "var", name: "app2" },
  filename: "remoteEntry.js",
  exposes: { "./Button": "./src/Button" },
  shared: { react: { singleton: true }, "react-dom": { singleton: true } }
});

At runtime the host calls await import('app2/Button'), which triggers the loading of remoteEntry.js, registers the module, and resolves the component.

Bit

Bit provides an npm‑like platform for component versioning, documentation, and CI/CD. Components can be imported into any project with bit import and published with bit export, supporting both UI components and plain JS/TS code.

// before
export function Button({ text }: ButtonProps) { return <div>{text}</div>; }
// after
export function Button({ text }: ButtonProps) { return <button type="button">{text}</button>; }

After tagging and exporting, the updated component appears in the Bit component marketplace for consumption.

Overall Summary

The article demonstrates how micro‑frontend engineering, Garfish, Module Federation, and Bit can be combined to share UI components across different frameworks and repositories, highlighting the underlying principles of bridging modules, managing shared state, and the trade‑offs of each approach.

References

Garfish loadComponent API

Webpack Module Federation documentation

Module Federation demo repository

Umi MFSU performance optimization

Bit official getting‑started guide

Exploration of Module Federation in Tencent Docs

How Webpack bundles run in browsers

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

micro-frontendModule FederationFrontend ArchitectureBitcomponent sharing
ELab Team
Written by

ELab Team

Sharing fresh technical insights

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.