Deeplinks Hottake
Iframes are the right answer and we keep pretending they are not
TLDR
The web has had iframe isolation for thirty years. Every time someone needs to embed one app inside another, they reach for web components, shadow DOM, micro-frontends, module federation, single-spa, qiankun, anything except the iframe. I just spent three weeks debugging a custom web component that wiped document.body.innerHTML to inject an app into a sub-page. The iframe replacement was 14 lines of HTML.
What people say about iframes
"Iframes are heavy." Heavy compared to what. Module federation pulls a runtime, a manifest, a fallback chain, and a hot-reload coordinator. An iframe is a <iframe src="…"> tag.
"Iframes break SEO." Iframes do not break SEO. The wrapper HTML still gets indexed. Google reads the <title>, the meta description, the schema.org breadcrumbs, the canonical link. What Google does not index is the iframe content, which is fine, because that content already has its own canonical URL where Google indexes it separately.
"Iframes break communication." This one was true in 2008. It has not been true since postMessage shipped. Every messaging library people build to talk between micro-frontends is just postMessage with a bigger surface area and worse browser DevTools support.
"Iframes break styling." Yes. That is the point. The wrapper has its own styles, the embedded app has its own styles, they cannot leak into each other. This is the feature you wanted from CSS modules, CSS-in-JS, shadow DOM, and namespacing prefixes. It already exists.
What people build instead
Web component shells that fetch the canonical app's HTML, parse it with DOMParser, manually re-execute inline scripts, attempt to deduplicate head assets, and pray the canonical app's init does not race with the body-injection step.
Micro-frontend frameworks that ship a 200KB runtime to coordinate route changes between three apps that could have been three iframes.
Custom shadow DOM components that wrap third-party libraries which then break because the library queries document.body instead of the shadow root.
All of these solve problems that iframes already solved. They exist because "iframe" sounds like 2008 and nobody wants to be the engineer who shipped an iframe in 2026.
Why the wrapper actually failed
I wrote gx-pdf-shell because I thought a 60-line web component was more "modern" than a 14-line iframe wrapper. The component did exactly one thing the iframe could not: it shared document.body with the canonical app, so URL changes, focus, and global state did not cross a frame boundary.
That sharing was the bug. The canonical app's app.js ran its init against a body that the shell then replaced. Half the handlers attached to dead DOM nodes. Extraction never fired. Export was silent.
If I had used an iframe from day one, the canonical app would have loaded in its own document, attached handlers to its own body, and had no idea it was being embedded. The wrapper's SEO <head> would have been completely separate from the canonical's runtime. No race. No DOM swap. No three weeks of intermittent symptoms.
When iframes are actually wrong
When you need shared scroll. When you need parent and child to coordinate at sub-millisecond latency. When you need DOM measurements that span the boundary. When the embedded content is one tiny widget, not an app.
These are real cases. They are also rare. Most "we need a frontend integration" is one app embedded inside another with occasional messages crossing the boundary. That is the iframe use case. It is fine.
The professional move
Stop being embarrassed by iframes. They are a battle-tested isolation primitive that ships in every browser. They handle CSP, sandbox, focus management, accessibility, and DevTools natively. They cost less complexity than every alternative.
If your architecture review meeting hears "iframe" and the response is "we should look at micro-frontends," ask what specific problem the micro-frontend framework solves that the iframe does not. Most of the time the answer is "I do not know, but iframes feel old."
Old works. Old shipped. Old debugs in three seconds with the DevTools you already know. Pick old.