When microfrontends are composed on the client using native Web Components (custom elements with Shadow DOM) as the integration contract, what isolation does Shadow DOM actually provide, and what does it NOT protect against compared to a full cross-origin iframe boundary?
answer
- attachShadow open vs closed mode
- CSS custom properties pierce shadow boundary
- event retargeting at shadow boundary
- custom-element registry is global and append-only
- no JS realm isolation
basics
~20 sShadow DOM keeps each microfrontend's CSS and internal markup from leaking into or being affected by the rest of the page, like a bubble around its styles and structure. But it does not stop its JavaScript from touching the same page-wide window object, unlike an iframe which blocks that too.
solid answer
~40 sCustom elements with Shadow DOM give strong style and DOM-structure encapsulation — CSS rules inside the shadow tree don't leak out and page-level CSS mostly can't reach in, aside from inherited properties and CSS custom properties which deliberately pierce the boundary — so teams get iframe-like visual isolation without iframe's UX cost, since it's real DOM inside the same document. What it does NOT provide is JavaScript execution isolation: all custom elements on a page still run in one JS realm, sharing window, global prototypes, and any un-namespaced globals, so a memory leak, an infinite loop, or a global variable collision in one microfrontend's component can still degrade or break the rest of the page — exactly the class of risk a cross-origin iframe eliminates via the browser's process/origin sandbox.
go deeper
Should know Shadow DOM keeps a microfrontend's CSS and markup from leaking, without needing to explain the exact mechanism.
Should be able to describe attaching a shadow root and open vs closed mode at a basic level, and know that JavaScript still shares the page's global scope.
Should explain event retargeting, CSS custom-property piercing, and the custom-element registry collision problem, and reason about when web components are or aren't sufficient isolation versus iframes.
Should connect the isolation gap to organizational governance — naming conventions, lint enforcement, error-boundary strategy — and make the call on when a genuinely untrusted integration needs to escalate to iframe-grade isolation regardless of web-components' developer-experience advantages.
## The integration contract Web Components give microfrontend composition a **browser-native integration contract**: each microfrontend registers a custom element via the Custom Elements API, and the host page simply drops that tag into its markup wherever the microfrontend should render — no bespoke mounting API, no framework-specific adapter, just an HTML tag any framework (or no framework) can consume. Inside that custom element, the microfrontend typically attaches a Shadow DOM, which creates a separate, encapsulated DOM subtree hung off the custom element. ## What Shadow DOM actually isolates Shadow DOM's isolation is **real and specific**: - **CSS selectors written inside the shadow tree** cannot select elements outside it, and — critically for microfrontends, where five teams' CSS living on one page is normally a recipe for cascading collisions — CSS rules defined at the document level do not apply inside the shadow tree either, aside from a deliberately designed set of exceptions. - **Inherited CSS properties** (like `font-family` or `color`) still cascade in through the boundary, and **CSS custom properties** are explicitly designed to pierce the shadow boundary so a host page can pass design tokens down to a microfrontend without breaking encapsulation. - **Querying the document** for elements does not reach into a shadow tree by default in open mode without explicitly walking into the element's shadow root, and closed mode additionally hides that shadow root property, blocking even that explicit walk. - **DOM events** that originate inside the shadow tree are retargeted as they cross the boundary, so listeners on the host page see the event as coming from the custom element itself, not from whatever internal element actually fired it — this preserves encapsulation of internal structure while still letting the host react to, say, a click. | Mode | Reaching the shadow root from a document query | |---|---| | open | the shadow root, by explicitly walking into the element and querying from there | | closed | the shadow root property itself is hidden, blocking even that explicit walk | ## Where the isolation stops This gives teams most of the visual and structural benefits people reach for iframes for — no CSS bleed in either direction, internal markup structure hidden from the host's DOM queries and devtools inspection by default — while staying in the same document, same DOM tree, same JS realm as the rest of the page. That last point is exactly where the isolation stops. Every custom element on the page, regardless of which team wrote it, executes as regular JavaScript in **one shared global scope**: they all see - the same `window`, - the same built-in prototypes, - the same document-level event loop. A microfrontend with a runaway timer, a memory leak, or a script that overwrites a shared built-in prototype method for convenience affects every other microfrontend on the page exactly as it would with plain JS-bundle mounting — Shadow DOM does nothing to stop it, because Shadow DOM is a DOM/CSS isolation primitive, not a JavaScript execution sandbox. A cross-origin iframe, by contrast, gets a genuinely separate JS realm enforced by the browser's process/origin model, so this class of failure literally cannot cross that boundary uninvited. ## The Custom Elements registry collision A second, easy-to-miss failure mode is specific to the Custom Elements registry: registering a custom element is a **single, page-global, append-only operation** — a given tag name can only be defined once per document, and calling define twice with the same name throws. In a microfrontend world where two independently-deployed teams might both want to register the same tag name (or, worse, two different versions of the same team's component get loaded together during a rolling deploy), this becomes a real coordination problem: teams must adopt naming conventions such as prefixing tags with a team or product namespace, or a second microfrontend's registration attempt throws at runtime and its widget silently fails to render — a bug that, like the shared-global collision above, only shows up when two specific microfrontends happen to be composed on the same page together, making it hard to catch in isolated per-team testing. ## The middle position on the spectrum The practical upshot is that Web Components sit in a genuine **middle position on the isolation spectrum**: stronger than a bare JS-bundle mount for CSS and DOM structure, but no stronger than that same bare mount for JavaScript execution and global-scope safety. Teams choosing this approach are usually optimizing for a specific pain point: - CSS collisions across independently-styled teams, - or framework-agnostic composition so a React-based host can embed a Vue-authored widget without a framework adapter, while accepting that isolation for buggy or malicious code still needs other tools: - strict lint rules against modifying globals or built-in prototypes, - careful namespacing of the Custom Elements registry, - and treating a genuinely untrusted third party as an iframe case regardless of how appealing the web-components developer experience is. This is why organizations that adopt web-components-based microfrontends (a pattern popularized alongside compiler tooling like Stencil that outputs standards-based custom elements) usually pair it with strong internal governance over global mutations and naming, rather than treating it as a substitute for iframe-grade security isolation.
- Why doesn't a page-level DOM query find an element that's inside another microfrontend's open shadow root?Open shadow roots still stop normal DOM tree traversal and query methods from crossing the boundary by default — you'd have to explicitly walk into the element's shadow root and query from there. This is what gives shadow DOM its structural encapsulation even in open mode; closed mode additionally hides the shadow root property itself so even that explicit walk is blocked.
- How would you prevent two microfrontend teams from ever colliding on the Custom Elements registry in a large organization?Enforce a naming convention that prefixes every custom element tag with a team or product namespace, and treat the registry as a shared namespace requiring the same kind of coordination as a shared package registry — ideally with a lightweight internal catalog or lint rule that flags unprefixed or duplicate tag-name registrations before deploy, since the failure only surfaces at runtime when two specific microfrontends are composed together.
- If Shadow DOM doesn't isolate JavaScript execution, what's a lighter-weight mitigation than iframes for reducing the blast radius of one microfrontend's JS bugs on the rest of the page?Common mitigations include strict linting/CI checks that forbid mutating global objects or built-in prototypes, using bundlers that scope module-level variables to avoid accidental globals, wrapping mount/unmount lifecycle calls in per-microfrontend error boundaries, and monitoring so one team's uncaught exception doesn't silently take down others' rendering. None of these are a hard security boundary the way a cross-origin iframe is, so they're appropriate for trusted internal teams, not for genuinely untrusted code.
Shadow DOM is like a soundproofed, sealed display case inside a shared showroom floor — visitors can't see the case's internal wiring and the case's own paint job won't bleed onto the showroom floor, but if a fire starts inside the case, it's still burning in the same building as everything else, unlike a separate booth in its own room.
saying these in an interview costs you the question
- Believes Shadow DOM isolates JavaScript execution the same way a cross-origin iframe does
- Doesn't know CSS custom properties are designed to cross the shadow boundary
- Thinks a custom element tag name can be registered multiple times without conflict
- Can't explain event retargeting across the shadow boundary
- Recommends web components as sufficient isolation for embedding genuinely untrusted third-party code