DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Hardware-Accelerated OpenGL Rendering in a Linux Container
  • Lift-and-Shift vs. Modernize: A Decision Framework for Enterprise Workloads
  • Compliance Reporting Without Losing the Spreadsheet or the Control
  • An XGBoost Property Valuation Postmortem: Leakage, Overfitting, and SHAP Surprises

Trending

  • How to Write for DZone Publications: Trend Reports and Refcards
  • Securing Branch Networks With Firewalls, VPNs, IDS/IPS, and Identity-Based Access
  • Microservices Architecture in Production: 7 Engineering Decisions That Determine Success or Failure
  • A Practical Guide to Temporal Workflow Design Patterns
  1. DZone
  2. Culture and Methodologies
  3. Methodologies
  4. I Got Tired of Copy-Pasting Microfrontend Boilerplate, So I Built a Bridge

I Got Tired of Copy-Pasting Microfrontend Boilerplate, So I Built a Bridge

A tiny, type-safe bridge for mounting React microfrontends across Module Federation boundaries — without repetitive lifecycle wrappers, shared stores, or code generation.

By 
Vitaly Zheltko user avatar
Vitaly Zheltko
·
Aug. 10, 26 · Tutorial
Likes (0)
Comment
Save
Tweet
Share
87 Views

Join the DZone community and get the full member experience.

Join For Free

When we started to work on microfrontend migration on one of our projects, the architecture looked great on paper (like always): one host shell, several remote apps, and teams could deploy independently on their own timelines.

But in practice it wasn't so clean. One part kept getting on my nerves: actually mounting remote React components inside the host. Each microfrontend came with the same glue code. Load the remote bundle, create a React root, render the component, keep track of the mounted instance, push updated props into it when the host re-renders, and clean up listeners on unmount. And do not forget to handle load failures. It wasn't especially hard code. But it was just the kind of code nobody wants to repeat.

Another problem is type safety, which had a habit of disappearing exactly where I wanted it most. Inside the remote, TypeScript understood the component props perfectly. But at the host boundary, that often collapsed into unknown and as any. If a remote added a required prop or renamed an existing one, the host usually did not find out from the compiler. After doing this a few times across different projects, I decided the pattern deserved a real abstraction instead of one more copy-pasted wrapper.

What I Wanted

It should be part of my toolkit package and shouldn't be really hard. Something much more practical. The goal was simple:

  • Remove repetitive host-side boilerplate
  • Keep prop types across the host/remote boundary
  • Work with separate bundles and separate React roots
  • Avoid shared stores, global registries, and code generation
  • Fit into an existing Module Federation setup without changing how remotes are versioned or deployed

That idea transformed to @mf-toolkit/mf-bridge.

The Base

The package has two parts: one wrapper on the remote side, and one host component that takes care of the integration.

On the remote side, you define the entry once:

TypeScript
 
import { createMFEntry } from '@mf-toolkit/mf-bridge/entry' 
import { CheckoutWidget } from './CheckoutWidget' 

export const register = createMFEntry(CheckoutWidget)
On the host side, you render the bridge where the remote should appear:
import { MFBridgeLazy } from '@mf-toolkit/mf-bridge'

<MFBridgeLazy 
  register={() => import('checkout/entry').then(m => m.register)} 
  props={{ orderId, userId }} 
  fallback={<CheckoutSkeleton/>} 
/>


That’s all. With MFBridgeLazy, the host doesn’t have to deal with all the hassle of loading things on demand, setting up the root, updating stuff, cleaning up, or handling event listeners — the tool does it all. Plus, because the register function has clear types, the host can automatically figure out what props the remote component needs.

If the remote component suddenly needs a new prop, you’ll see a TypeScript error right away during development, not after the app is already live and causing problems.

How Prop Updates Travel

This was the part I wanted to keep as boring and predictable as possible.

Once a remote component is mounted, it lives in its own React root. That means the host cannot simply re-render it as if it were a normal local child. The host still needs a way to send updated props into that remote tree every time its own state changes.

There are plenty of ways to solve this: shared stores, shared context, global event buses, custom registries. I wanted the smallest possible mechanism that stayed local to each mounted microfrontend.

So `mf-bridge` uses the one thing both sides already share: the mount element.

Microfrontend communication flow

When the host re-renders with new props, the bridge dispatches a `CustomEvent` on that specific DOM element. The remote listens to events on that same element and re-renders with the new props. That is it.

I like this approach for a few reasons.

First, it is naturally isolated. If you have several microfrontend slots on the same page, each one has its own mount element, so updates do not bleed across instances.

Second, it does not need a shared module graph or global state container just to move props around.

Third, it keeps the contract very explicit: the host owns the mount point, and the props, and the remote owns how it renders them.

Internally, the package wraps this in a small typed DOM event bus, but consumers do not really need to think about those details.

Why This Helped More Than Just Saving Lines of Code

The obvious benefit is less boilerplate. If a page has five remote slots, I no longer end up with five slightly different wrappers all doing the same lifecycle work.

But the bigger benefit is moving problems earlier in the process.

Before this, the host/remote boundary was often exactly where type information got blurry. That made one of the most important contracts in the system feel surprisingly fragile. A remote could evolve, and the host would not always know it had fallen out of sync.

With mf-bridge, prop inference flows from the remote entry to the host usage. That changes the feedback loop. A contract mismatch becomes a compile-time problem instead of an incident report.

Type-safe microfrontend integration

There is also a reliability benefit in the lifecycle handling. The package takes care of the repetitive, easy-to-forget parts:

  • Lazy loading with a fallback UI
  • Clean mount and unmount behavior
  • Prop streaming on re-renders
  • Listener cleanup
  • Error handling when the remote fails to load
  • Optional preloading and retry behavior
  • Optional hooks for setup and teardown on the remote side when you need DI or per-mount initialization

None of these features are individually groundbreaking. The value is that they come together in one small, reusable bridge instead of being re-implemented in every host wrapper.

The Cases I Wanted to Be Sure About

When the basic version started to work, I spent a bit more time on some of the scenarios that usually make microfrontend wrappers fragile.

One of those cases was multiple instances of the same remote on a single page — a widget in the main content area, a compact version in a sidebar, or the same remote mounted in a few different places. I wanted to make sure what updates stayed local to the exact mount point instead of leaking. Using the DOM element itself as the transport turned out to be a very practical way to preserve that isolation.

Another important case was failed loading. I didn't want the host to end up with a blank hole in the UI just because a remote bundle failed on the first attempt. That is why the bridge supports fallbacks, preloading, and retry behavior. I think that kind of thing makes an integration feel solid.

And sure, we should not forget about what happens when the problem is rendering. If a remote drops during render, I do not want that failure to destabilize the whole host page. So error handling became part of the design too: we keep the failure contained to the mount point, surface the error to the host, and make recovery possible when new props arrive. Then there is setup and unmount — that case is covered, too.

Microfrontend lifecycle flow

Where It Fits Compared to React.lazy or Portals

This package is not a replacement for React.lazy, and it is not trying to be cleverer than React.

If your component lives in the same bundle and the same React tree, React.lazy is still the natural tool. If you just want to render into a different DOM node inside the same tree, portals are great.

mf-bridge is for the awkward case those tools do not cover well: a component living across a Module Federation boundary, loaded from a separate bundle, mounted into its own React root, but still expected to behave like a first-class part of the host page.

That is the gap I wanted to close.

A Small Package, Not a New Platform

I also cared quite a bit about keeping the package lightweight. It has zero production dependencies and uses the browser's native CustomEvent API for prop streaming. In practice, that means less surface area, fewer moving parts, and one less utility layer to debug when something goes wrong.

The goal was never to build a microfrontend platform. It was simply to remove a recurring nuisance and make the host/remote boundary feel safer.

Sometimes that is enough to justify a package.

I published it as @mf-toolkit/mf-bridge.

Repository, docs, and examples: github.com/zvitaly7/mf-toolkit.

If you are working with Module Federation and you already have a small pile of hand-written wrappers around remote React components, this may save you some time. And if you have solved the same problem in a completely different way, I would genuinely be curious to compare notes.

Requirements engineering Host (Unix) remote

Published at DZone with permission of Vitaly Zheltko. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Hardware-Accelerated OpenGL Rendering in a Linux Container
  • Lift-and-Shift vs. Modernize: A Decision Framework for Enterprise Workloads
  • Compliance Reporting Without Losing the Spreadsheet or the Control
  • An XGBoost Property Valuation Postmortem: Leakage, Overfitting, and SHAP Surprises

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook