In a microfrontend architecture, what is the basic difference between mounting a microfrontend as a JavaScript bundle directly into the DOM versus embedding it via an iframe?
answer
- same DOM vs separate document
- postMessage handshake
- shared framework instance via module federation
- blast-radius containment
- iframe SEO weakness
basics
~20 sJS mount loads the microfrontend's code into the same page so it shares the DOM and can talk to other parts easily. An iframe loads it as a separate mini-webpage inside a box, fully isolated but harder to communicate with.
solid answer
~30 sJS-bundle mounting (e.g., via single-spa or Module Federation) loads each microfrontend's JS into the host page's own DOM/JS realm, giving shared routing, shared dependencies, and cheap in-memory communication (custom events, shared state), but risking CSS/JS collisions and requiring careful dependency management. Iframe embedding gives full isolation — separate DOM, CSS, JS globals, even separate origin — so a broken or malicious microfrontend can't touch the host, at the cost of duplicated framework payloads, awkward cross-frame messaging (postMessage), inconsistent viewport/scroll behavior, and worse accessibility/SEO.
go deeper
Should describe the surface difference — one shares the page, one is a sandboxed sub-page — and give one reason to pick each (seamless UX vs isolation).
Should name concrete mechanisms (custom events/shared bus for JS mount, postMessage for iframe) and at least one concrete cost of each (CSS/global collisions vs duplicated framework payload).
Should reason about version-skew coordination costs in JS mounting (shared-dependency resolution) and layout/accessibility friction in iframes, and pick the right tool given a stated isolation requirement.
Should connect the choice to org-level concerns — team autonomy vs blast-radius containment, security boundary requirements for third-party embeds, and how the decision constrains future composition options.
## What composition means Microfrontend composition is the mechanism by which independently built and deployed frontend pieces are assembled into one coherent page or application for the end user. Two of the most common **client-side composition primitives** are direct JavaScript mounting and iframe embedding, and the choice between them sits at the center of nearly every microfrontend trade-off discussion. ## The two primitives side by side | Property | JavaScript-bundle mounting | Iframe embedding | |---|---|---| | Browsing context | the same context as the shell | a separate context, with its own DOM and CSS cascade | | Communication | custom DOM events, a shared pub/sub bus, imported utility functions | `postMessage`, asynchronous and string-serializable | | Framework runtime | shared instances via module federation | its own copy per iframe | | Isolation | developer discipline and naming conventions | enforced by the browser itself | | Deep-linking, back-button behavior, and accessibility | automatic with same-document JS mounting | deliberate extra work | | SEO | — | weaker by default | ## How JavaScript-bundle mounting works With **JavaScript-bundle mounting**, each microfrontend ships as a set of JS/CSS assets that the host application (or an orchestrator library such as `single-spa`, or a bundler-level mechanism like Webpack/Rollup Module Federation) fetches and executes inside the same browsing context as the shell. Concretely: 1. The host page loads a root HTML document. 2. It dynamically imports each microfrontend's JS bundle. 3. It calls a lifecycle hook (`mount/bootstrap/unmount`). 4. The microfrontend renders its markup into a designated DOM node that already lives on the page. Because everything executes in one JS realm and one DOM tree, microfrontends can share a router, a design-token CSS layer, even framework runtime instances (React, Vue) via module federation's shared-scope mechanism, and they can communicate cheaply: - custom DOM events - a shared pub/sub bus - directly imported utility functions This tight coupling is exactly why it's popular for cohesive product experiences: navigating from a search microfrontend to a cart microfrontend feels instant because there's no full page reload and no cross-boundary handshake. ## The cost of that cohesion The cost of that cohesion is the **absence of a hard isolation boundary**. - Every microfrontend's CSS can leak into every other's (mitigated but not eliminated by CSS Modules, Shadow DOM, or naming conventions like BEM). - Every microfrontend's JavaScript runs with the same global `window` object, so one team's global assignment can silently collide with another's. - A memory leak or infinite loop in one microfrontend can degrade or crash the whole page. - **Version skew** is also a live problem: if two microfrontends need incompatible major versions of the same shared dependency, module federation's shared-scope resolution has to pick one (or duplicate the dependency, defeating the point), which becomes a genuine coordination cost between teams that are supposed to be independent. ## What the iframe boundary buys Iframe-based composition takes the opposite trade-off. Each microfrontend is served as its own standalone HTML document loaded into an `iframe` element on the host page. Because an iframe is a **separate browsing context**, it gets: - its own DOM - its own CSS cascade - its own JavaScript global scope - and — if served from a different origin — its own same-origin-policy sandbox enforced by the browser itself, not by developer discipline This is the strongest isolation guarantee available in a browser: a runaway script, a CSS reset, or even a security exploit inside the iframe cannot reach into the host page or sibling iframes unless the host explicitly opens a channel. That's why iframes remain the tool of choice when isolation is a hard requirement — embedding a genuinely untrusted third-party widget (an ad, a payment form from a different vendor, a partner's embedded dashboard) is a textbook iframe use case. ## What the iframe boundary costs The costs show up in user experience and engineering ergonomics. - **Communication** across the iframe boundary requires `postMessage`, an asynchronous, string-serializable API that both sides must agree on — there's no calling a function or sharing an object reference across the boundary. - Each iframe typically **re-downloads and re-instantiates** its own copy of any shared framework runtime, so if five microfrontends on a page each iframe-embed their own React, the user pays for five React downloads and five separate render trees, hurting Time-to-Interactive and memory footprint. - **Layout** is awkward: an iframe has a box with its own scrollbar by default, so making content flow naturally with the rest of the page (variable height, shared scrolling, focus management, printing) needs extra plumbing such as resize-observer-based height syncing. - Deep-linking, back-button behavior, and **accessibility** (focus can get trapped at an iframe boundary) all need deliberate extra work that's automatic with same-document JS mounting. - **SEO** is also weaker by default, since search crawlers historically render iframe content inconsistently or attribute it separately from the parent document. ## Which one to reach for In practice, teams pick JS-bundle mounting when the microfrontends are first-party, trusted, and need to feel like one seamless product. They reach for iframes specifically when isolation trumps cohesion: - embedding a third party - isolating a legacy app that can't be safely refactored to share a DOM - building an internal tool catalog where blast-radius containment matters more than a seamless UX Recognizing which failure mode you're optimizing against — a bug in one team's code breaking everyone's page versus a page that feels like several different websites stitched together — is usually the deciding question in an interview setting.
- How does Webpack/Module Federation try to avoid duplicate framework downloads that plain iframe composition suffers from?Module Federation lets microfrontends declare shared dependencies (like React) in a shared scope with semver ranges; at runtime the host resolves a single compatible instance and only downloads it once, injecting it into every consuming microfrontend instead of each bundling its own copy. If versions are incompatible it falls back to loading both, which reintroduces duplication, so teams still need to coordinate major-version bumps of shared libraries.
- If you must use iframes but want the microfrontend to blend into the page layout with auto height and no visible scrollbar, how would you achieve that?The iframe's own document posts its content height to the parent via postMessage (or the parent uses a ResizeObserver on a proxy element), and the parent then sets the iframe element's height to match, disabling the iframe's internal scrollbar with CSS overflow:hidden. This has to be re-triggered on content changes, which adds ongoing plumbing the JS-mount approach doesn't need.
- What's a scenario where JS-bundle mounting's shared-global-scope risk actually causes a production incident?Two teams both attach a convenience library to a shared global variable on window; when both microfrontends load on the same page, the second one silently overwrites the first's version, producing subtle bugs only visible when both are present together — a class of bug that passes each team's isolated tests but fails in the combined page, exactly what hard iframe isolation would have prevented.
JS mounting is like several chefs cooking in the same open kitchen sharing pots and heat — fast handoffs but a spill affects everyone; an iframe is like each chef working in a sealed kitchen booth passing only notes under the door — safe but slower and with duplicated equipment.
saying these in an interview costs you the question
- Says iframes and JS mounting are basically interchangeable performance-wise
- Doesn't mention that JS mounting shares one JS global/window
- Thinks iframes automatically share cookies/session with the parent regardless of origin
- Can't name postMessage as the iframe communication mechanism
- Assumes duplicate framework downloads only happen with iframes, never with JS mounting