home/blogs/micro-frontends-with-vite-module-federation-distributed-ui-and-cross-framework-integration

Micro Frontends with Vite: Module Federation, Distributed UI, and Cross-Framework Integration

A practical look at microfrontend architecture using Vite and Module Federation, followed by a cross-framework example of using a React package inside a Vue application.

Swapnesh Panda
• September 22, 2026• 4 min read• 96 views
Micro Frontends with Vite: Module Federation, Distributed UI, and Cross-Framework Integration

Micro Frontends: From Distributed Frontends to Cross-Framework Integration

1. Why Do We Need Micro Frontends?

As a frontend grows, the main problem is often coupling, not code size.

Consider an application with multiple business domains:

                    Frontend Application
                           │
        ┌──────────────────┼──────────────────┐
        │                  │                  │
     Products           Payments            Admin

In a traditional setup, these areas may share the same codebase, build, and deployment.

That becomes difficult when different teams need different release cycles or ownership boundaries.

One codebase
     +
One build
     +
One deployment

Microfrontends allow us to split the frontend around meaningful boundaries:

                    Host / Shell
                         │
             ┌───────────┼───────────┐
             │           │           │
          Products     Payments     Admin
           Remote       Remote      Remote

The value is not simply having multiple frontend projects. The value is allowing different parts of the system to evolve more independently.

Typical reasons include:

  • independent team ownership

  • independent release cycles

  • gradual migration from an existing frontend

  • separating large business domains

However, a large frontend does not automatically require microfrontends.

If one team can manage the application effectively as a single unit, introducing multiple applications may add unnecessary complexity.

The useful question is:

Do we have a boundary worth maintaining independently?


2. Building Microfrontends with Vite and Module Federation

Once we have that boundary, we need a way for independently built applications to consume each other's modules.

For this example:

Vite
+
@module-federation/vite

Reference implementation:

scaflo/monorepo-microfrontend-example

The example follows the host/remote model.

The remote exposes a module:

exposes: {
  "./App": "./src/App",
}

The host then consumes it through Module Federation.

Host
 │
 │ requests
 ▼
remoteEntry.js
 │
 ▼
Remote module

We do not need the entire repository configuration here. The important pieces are the exposed module, the remote entry, and the host configuration.

Exposing a module

The remote can define:

federation({
  name: "remote",

  filename: "remoteEntry.js",

  exposes: {
    "./App": "./src/App",
  },
});

The host can then reference:

import RemoteApp from "remote/App";

Although the syntax looks like a normal import, the module is resolved from the federated remote rather than from the host's local source tree.

Sharing dependencies

If both applications use React, the federation configuration can also declare shared dependencies:

shared: [
  "react",
  "react-dom",
]

This lets the federation runtime handle these dependencies across the host and remote instead of treating every copy independently.

What changes?

With a traditional frontend:

Application
    │
    ▼
Build
    │
    ▼
All modules

With Module Federation:

Host
 │
 ├── Local modules
 │
 └── Remote modules
          │
          ▼
     Other application

Part of the dependency graph can now come from another independently built application.

That is the key capability we need for this microfrontend setup.

But this still leaves another problem:

What happens when the functionality we need belongs to a different frontend ecosystem?


3. Cross-Framework Integration: Using a React Package Inside Vue

Suppose the application is built with Vue, but the package we want is built for React.

Vue Application
      │
      ▼
React Package

For this example, we can use Agentation, a React-based package for visual UI annotation and feedback.

Instead of rewriting the package, we can mount the React component inside the Vue application.

The boundary is simply a DOM element:

Vue
 │
 ▼
DOM element
 │
 ▼
React root
 │
 ▼
Agentation

The Vue component:

<script setup lang="ts">
import { onMounted, onUnmounted, ref } from "vue";

import React from "react";
import {
  createRoot,
  type Root,
} from "react-dom/client";

import { Agentation } from "agentation";

const container = ref<HTMLElement | null>(null);

let root: Root | null = null;

onMounted(() => {
  if (container.value) {
    root = createRoot(container.value);

    root.render(
      React.createElement(Agentation)
    );
  }
});

onUnmounted(() => {
  if (root) {
    root.unmount();
    root = null;
  }
});
</script>

<template>
  <div
    id="agentation-bridge-root"
    ref="container"
  />
</template>

The bridge starts with the Vue-managed container:

<div
  id="agentation-bridge-root"
  ref="container"
/>

Once Vue mounts it, React receives that DOM node:

root = createRoot(container.value);

and renders Agentation into it:

root.render(
  React.createElement(Agentation)
);

The result is:

Vue Application
│
└── #agentation-bridge-root
        │
        └── React
             │
             └── Agentation

We are not converting React into Vue.

Vue owns the surrounding application, while React manages its own subtree inside the container.

Lifecycle

Because Vue owns the component lifecycle, the React root must be cleaned up when the Vue component is removed:

onUnmounted(() => {
  if (root) {
    root.unmount();
    root = null;
  }
});

So:

Vue mounts
   ↓
createRoot()
   ↓
Render Agentation
   ↓
Vue unmounts
   ↓
root.unmount()

When does this make sense?

This approach is most useful for relatively isolated packages or utilities where a small integration boundary is enough.

For example:

Vue
 │
 └── Integration boundary
          │
          └── React utility

The trade-off is that the application now includes another runtime and framework ecosystem, so bundle size, dependency versions, CSS isolation, and lifecycle management still need to be considered.

The technique is useful because it gives us another option besides rewriting or replacing a package simply because it belongs to a different framework.


Tagged:#microfrontends#vite#module-federation#react#vue#frontend-architecture#javascript#typescript